Python的GIL:全局解释锁(Global Interpreter Lock)在执行多线程时,同一时刻至多只有一个线程被CPU执行,如果要实现并行可以使用多进程。
Python适用于IO密集型情况,不太适用于计算密集型的场景,因为执行计算时线程间的切换会耗费时间。
下面有三种情况
import threading import time def sub(): global num temp=num print('A',end='|') num=temp-1 print('B',end='|') num=30 l=[] for i in range(30): t=threading.Thread(target=sub) l.append(t) t.start() for t in l: t.join() print('\n',num) #结果为0
import threading import time def sub(): global num temp=num print('A',end='|') time.sleep(1) num=temp-1 print('B',end='|') num=30 l=[] for i in range(30): t=threading.Thread(target=sub) l.append(t) t.start() for t in l: t.join() print('\n',num) #结果为29
import threading import time def sub(): global num temp=num print('A',end='|') time.sleep(0.001) num=temp-1 print('B',end='|') num=30 l=[] for i in range(30): t=threading.Thread(target=sub) l.append(t) t.start() for t in l: t.join() print('\n',num) #每次的结果都不同
第一种情况,因为程序运行时间太短,所以线程间不用切换,相当于线程一个一个执行每次都减1故结果为0,从输出的AB情况也可以看出
第二种情况,当碰到sleep时解释器会进行线程的任务切换,而且sleep1秒的时间过长,导致每个线程的temp都拿到num的初始值30,然后全部进行减1操作将结果29又赋给num,所以最好num等于29,从输出的AB情况也可以看出
第三种情况,碰到sleep时开始切换线程,但是由于sleep时间短,只有部分线程temp拿到num的初始值30,然后程序又切回原来的线程进行减1运算,将结果赋给num,而后面的部分线程的temp获得新的num值,而后又切换回来做减1运算,然后后面的线程获得新的num值,如此往复直到结束。因为每次切换的时间点并不唯一,所以最后的num值也不一样。从AB的情况也可以看出。
要得到0的结果还可以用Lock锁
import threading import time def sub(): global num lock.acquire()#获取锁 temp=num print('A',end='|') time.sleep(0.01) num=temp-1 lock.release()#释放锁 print('B',end='|') lock=threading.Lock() num=30 l=[] for i in range(30): t=threading.Thread(target=sub) l.append(t) t.start() for t in l: t.join() print('\n',num)
在线程获取锁之后,只能执行锁下面的程序,直到锁被释放,才能执行其他线程
死锁的情况
import threading import time class MyThread(threading.Thread): def actionA(self): A.acquire() print(self.name,'gotA',time.ctime()) time.sleep(2) B.acquire() print(self.name,'gotB',time.ctime()) time.sleep(1) B.release() A.release() def actionB(self): B.acquire() print(self.name,'gotB',time.ctime()) time.sleep(2) A.acquire() print(self.name,'gotA',time.ctime()) time.sleep(1) A.release() B.release() def run(self): self.actionA() self.actionB() if __name__ == '__main__': A=threading.Lock() B=threading.Lock() l=[] for i in range(5): t=MyThread() l.append(t) t.start() for t in l: t.join() print('ending...')结果为
Thread-1 gotA Thu Apr 19 20:14:41 2018#线程1开始执行actionA,获得A锁 Thread-1 gotB Thu Apr 19 20:14:43 2018#线程1,获得B锁,并于1秒后释放B锁和A锁 Thread-1 gotB Thu Apr 19 20:14:44 2018#线程1执行actionB,获得B锁,碰到sleep,进行线程切换 Thread-2 gotA Thu Apr 19 20:14:44 2018#线程2开始执行actionA,获得A锁 #线程1要继续运行就要获得A锁,线程2要继续运行就要获得B锁,但是锁都到对方手中,所以造成程序无法继续执行,就是死锁
要解决以上问题可以用递归锁
import threading import time class MyThread(threading.Thread):#用类来实现多线程,必须继承threading.Thread,且必须定义run函数,因为t.start()就是调用run函数 def actionA(self): r_lock.acquire()#count=1 print(self.name,'gotA',time.ctime()) time.sleep(2) r_lock.acquire()#count=2 print(self.name,'gotB',time.ctime()) time.sleep(1) r_lock.release()#count=1 r_lock.release()#count=0 def actionB(self): r_lock.acquire() print(self.name,'gotB',time.ctime()) time.sleep(2) r_lock.acquire() print(self.name,'gotA',time.ctime()) time.sleep(1) r_lock.release() r_lock.release() def run(self): self.actionA() self.actionB() if __name__ == '__main__': r_lock=threading.RLock() l=[] for i in range(5): t=MyThread() l.append(t) t.start() for t in l: t.join() print('ending...')
递归锁里有个计数器,当递归锁被acquire一次计数器就加一,release一次计数器就减1。
只有当计数器等于0时才能被其他的线程获得,大于0时只能执行现在获得锁的线程,直到计数器再次变为0时,其他线程才有可能获得递归锁,解决了死锁的情况