GIL: global interpreter lock (cpython)
GIL控制的字节码的执行,锁控制的是Python代码
- 什么是字节码,怎么查看字节码?
#通过dis模块查看函数add的字节码 import dis def add(a): a = a+1 return a print(dis.dis(add)) #运行结果 6 0 LOAD_FAST 0 (a) 2 LOAD_CONST 1 (1) 4 BINARY_ADD 6 STORE_FAST 0 (a) 7 8 LOAD_FAST 0 (a) 10 RETURN_VALUE
2.GIL作用
GIL使得同一时刻只有一个线程在一个cpu上执行字节码。保证字节码的执行是线程安全的
有了GIL是不是就是多线程安全的,不需要考虑线程间同步呢?
答案肯定不是的,因为GIL会在适当的时候释放的。举例说明
total=0 def add(): #1. dosomething1 #2. io操作 #3. dosomething3 global total for i in range(1000000): total +=1 def desc(): global total for i in range(1000000): total -=1 import threading thread1=threading.Thread(target=add) thread2=threading.Thread(target=desc) thread1.start() thread2.start() thread1.join() thread2.join() print(total) #运行结果:每次结果都不一样
不是说有GIL吗? 为什么还会出现这种现象呢?
首先,了解GIL的知识点:
- 对于有I/O操作的多线程,每次遇到I/O操作便会进行GIL锁的释放
- 如果是纯计算的程序,没有I/O操作,解释器会根据sys.setcheckinterval的设置来自动进行线程间的切换,默认情况下每隔100个时钟(python的内部时钟,对应于解释器执行的指令)就会释放GIL锁从而轮换到其他线程的执行
以简单实例说明 # a是全部共享变量 def add1(a): a += 1 def desc1(a): a -= 1 import dis print(dis.dis(add1)) print(dis.dis(desc1)) #运行结果 51 0 LOAD_FAST 0 (a) 2 LOAD_CONST 1 (1) 4 INPLACE_ADD 6 STORE_FAST 0 (a) 8 LOAD_CONST 0 (None) 10 RETURN_VALUE 55 0 LOAD_FAST 0 (a) 2 LOAD_CONST 1 (1) 4 INPLACE_SUBTRACT 6 STORE_FAST 0 (a) 8 LOAD_CONST 0 (None) 10 RETURN_VALUE # 使用伪代码来说明,考虑一种极端情况来解释 add1 """ 1. load a # a=0, 切换线程到desc1 ① 2. load 1 # 1 ③ 3. + # 1 ⑤ 4. 赋值给a # a=1 ⑦ """ desc1 """ 1. load a # a=0, 切换为add1 ② 2. load 1 # 1 ④ 3. - # -1 ⑥ 4. 赋值给a # a=-1 ⑧ """ 经过add1和desc1操作之后,a要不就是1或者-1.而不是结果0
总结:因为线程同步解决的是代码的线程安全性问题,而GIL解决的只是字节码的线程安全,概念不一样
文章来源: 11-1 Python中的GIL