全局解释器锁(Global Interpreter Lock ): 在 Python 语言的主流实现 CPython 中,GIL 是一个货真价实的全局线程锁,在解释器解释执行任何 Python 代码时,都需要先获得这把锁才行,在遇到 I/O 操作时会释放这把锁。如果是纯计算的程序,没有 I/O 操作,解释器会每隔 100 次操作就释放这把锁,让别的线程有机会执行(这个次数可以通过 sys.setcheckinterval 来调整)。所以虽然 CPython 的线程库直接封装操作系统的原生线程,但 CPython 进程做为一个整体,同一时间只会有一个获得了 GIL 的线程在跑,其它的线程都处于等待状态等着 GIL 的释放。即全局解释器锁保证同一时间只有一个线程在执行。
在多线程环境中,GIL用法:
1. 设置GIL
2. 切换到一个线程去运行
3. 运行:
a. 指定数量的字节码指令
b. 线程主动让出控制(可以调用time.sleep(0))
4. 把线程设置为睡眠状态
5. 解锁GIL
6. 再次重复以上所有步骤
例子:计算50个线程的执行个数
(1)未加锁之前:
import threading,time def run(n): #lock.acquire() #获取锁 global num time.sleep(0.5) num+=1 #time.sleep(1) #lock.release() #释放锁 #lock=threading.Lock() num=0 t_obj=[] for i in range(50): t=threading.Thread(target=run,args=("t-%s"%i,)) t.start() t_obj.append(t) for t in t_obj: t.join() print("----all thread has done....") print("num:",num)
在Python 2.x中答案:num可能会小于50. 原因:同一时间可能有多个线程在运行。而num只是加1.
(2)加锁处理:
import threading,time def run(n): lock.acquire() #获取锁 global num time.sleep(0.5) num+=1 time.sleep(1) lock.release() #释放锁 lock=threading.Lock() num=0 t_obj=[] for i in range(50): t=threading.Thread(target=run,args=("t-%s"%i,)) t.start() t_obj.append(t) for t in t_obj: t.join() print("----all thread has done....") print("num:",num)答案准确,num为50.
注意:在Python 3.x中答案:num始终为50.原因:解释器自己优化,内部已经加锁,保证同一时间只有一个线程在运行。