1.对主进程来讲,运行完毕指的是主进程代码运行完毕
2.对主线程来讲,运行完毕指的是主线程所在的进程内全部非守护线程通通运行完毕,主线程才算运行完毕
详细解释:
1.主进程在其代码结束后就已经算运行完毕了(守护进程在此时就被回收),而后主进程会一直等非守护的子进程都运行完毕后回收子进程的资源(不然会产生僵尸进程),才会结束,
2.主线程在其余非守护线程运行完毕后才算运行完毕(守护线程在此时就被回收)。由于主线程的结束意味着进程的结束,进程总体的资源都将被回收,而进程必须保证非守护线程都运行完毕后才能结束。
from threading import Thread,Lock import time mutex=Lock() x=100 def task(): global x mutex.acquire() temp=x time.sleep(0.1) x=temp-1 mutex.release() if __name__ == '__main__': start=time.time() t_l=[] for i in range(100): t=Thread(target=task) t_l.append(t) t.start() for t in t_l: t.join() print('主',x) #主 0 print(time.time()-start) #10.1311
mutexA=mutexB=RLock() 一个线程拿到锁,counter加1,该线程内又碰到加锁的状况,则counter继续加1,这期间全部其余线程都只能等待,等待该线程释放全部锁,即counter递减到0为止python
from threading import Thread,Lock,RLock import time # mutexA=Lock() # mutexB=Lock() #产生死锁 mutexA=mutexB=RLock() #递归锁 class MyThread(Thread): def run(self): self.f1() self.f2() def f1(self): mutexA.acquire() print('%s 拿到了A锁' %self.name) mutexB.acquire() print('%s 拿到了B锁' %self.name) mutexB.release() mutexA.release() def f2(self): mutexB.acquire() print('%s 拿到了B锁' %self.name) time.sleep(0.1) mutexA.acquire() print('%s 拿到了A锁' %self.name) mutexA.release() mutexB.release() if __name__ == '__main__': for i in range(10): t=MyThread() t.start() print('主')
同进程的同样,Semaphore管理一个内置的计数器,每当调用acquire( )时内置计数器-1;调用release() 时内置计数器+1; 计数器不能小于0;当计数器为0时,acquire()将阻塞线程直到其余线程调用release()web
# from multiprocessing import Semaphore from threading import Thread,Semaphore,current_thread import time,random sm=Semaphore(5) def go_wc(): sm.acquire() print('%s 上厕所ing' %current_thread().getName()) time.sleep(random.randint(1,3)) sm.release() if __name__ == '__main__': for i in range(23): t=Thread(target=go_wc) t.start()
与进程池是彻底不一样的概念,进程池Pool(4),最大只能产生4个进程,并且从头至尾都只是这四个进程,不会产生新的,而信号量是产生一堆线程/进程安全
GIL:全局解释器锁 GIL本质就是一把互斥锁,是夹在解释器身上的,每个python进程内都有这么一把锁,同一个进程内的全部线程都须要先抢到GIL锁,才能执行解释器代码多线程
GIL会对单进程下的多个线程形成什么样的影响: 多线程要想执行,首先须要争抢GIL,对全部待执行的线程来讲,GIL就至关于执行权限,同一时刻只有一个线程争抢成功,即单进程下的多个线程同一时刻只有一个在运行,意味着单进程下的多线程没有并行的效果,可是有并发的效果并发
ps:分散于不一样进程内的线程不会去争抢同一把GIL,只有同一个进程的多个线程才争抢同一把GILapp
为何要有GIL: Cpython解释器的内存管理机制不是线程安全的dom
GIL的优缺点: 优势: 保证Cpython解释器内存管理的线程安全socket
缺点: 同一进程内全部的线程同一时刻只能有一个执行,也就说Cpython解释器的多线程没法实现并行性能
GIL与自定义互斥锁的异同,多个线程争抢GIL与自定义互斥锁的过程分析: 相同:都是互斥锁 不一样点:GIL是加到解释器上的,做用于全局,自定义互斥锁做用于局部;单进程内的全部线程都会去抢GIL,单进程内的只有一部分线程会去抢自定义的互斥锁ui
单进程下的多个线程是没法并行,没法并行意味着不能利用多核优点
计算密集型应该使用多进程,如金融分析;IO密集型应该使用多线程,多核对性能的提高微不足道,如socket,爬虫,web
GIL保护的是解释器级的数据,保护用户本身的数据则须要本身加锁处理
from threading import Thread,Lock import time mutex=Lock() count=0 def task(): global count mutex.acquire() #GIL只能让线程先访问到解释器的代码,即拿到执行权限,而后将target的代码交给解释器的代 temp=count #码去执行,但线程拿不到mutex,一样要等待 time.sleep(0.1) count=temp+1 mutex.release() if __name__ == '__main__': t_l=[] for i in range(2): t=Thread(target=task) t_l.append(t) t.start() for t in t_l: t.join() print('主',count) #主 2