1.GIL介绍
GIL,意为全局解释器锁,是cPython执行多线程/进程计算密集型代码效果不如人意的主要原因。 cPython限制一个进程内同时只能执行一个线程。 首先介绍一下,正常多线程/进程执行时,多线程/进程数据混乱的原因: cpu分成多个时间片段,启动10线程,分配10个cpu时间片段,当我累加数字设置比较小的时候,在单个cpu时间片段内,for循环代码就执行完,就不会产生数据混乱的。当我数据设置的比较大时,在单个cpu时间片段内,for循环代码就执行不完,并且没有分配2个或2个以上的连续的cpu时间片段,导致一个cpu时间片段没有执行完该线程,下一个线程开始执行了 原文链接:https://blog.csdn.net/YZL40514131/article/details/126198377GIL是CPython才有的限制。
每个进程分配一个cPython解释器,垃圾回收功能也算解释器的一个线程,而且是线程不安全的。 所以cPython限制一个进程内同时只能执行一个线程,这就是GIL。 所以可以看出,GIL本质是个线程互斥锁。 GIL会在抢到锁后执行IO操作时被释放,所以IO密集型多线程/进程不受GIL影响,因为GIL会被不断释放。 Python的GIL(全局解释器锁)会在执行阻塞IO操作时被释放。这是因为在等待IO操作完成的过程中,Python线程不需要持有GIL,也不需要执行Python字节码。所以,在这种情况下,其他Python线程可以获得GIL并执行代码。
2.测试代码
#多线程执行以下代码,结果正常,因为GIL充当互斥锁。
def task():
global g_n
tem=g_n
# time.sleep(0.05)
g_n=tem-1
#以上代码等价于
def task():
global g_n
mutex.acquire()
tem=g_n
# time.sleep(0.05)
g_n=tem-1
mutex.release()
#但是time.sleep()执行后,出现数据混乱的问题,因为sleep是io操作,io操作提前释放了该线程的GIL,以至于没有互斥作用
def task():
global g_n
tem=g_n
time.sleep(0.05)
g_n=tem-1
#所以需要自己手动加上互斥锁
def task():
global g_n
mutex.acquire()
tem=g_n
time.sleep(0.05)
g_n=tem-1
mutex.release()#结果正确
完整测试代码:
import os
import random
import threading
import multiprocessing
import time
from multiprocessing import Process
from threading import Thread
g_n=180#初始值
cnt=int(10)
cpu_cnt=os.cpu_count()
mutex=threading.Lock()
def task():
global g_n
mutex.acquire()
tem=g_n#取出全局变量的值
# time.sleep(0.05)
g_n=tem-1#每次减一
mutex.release()
if __name__ == '__main__':
l_p=[]
print("cpu_cnt: ",cpu_cnt)
for _ in range(cpu_cnt):#创建cpu核数个线程
t=Thread(target=task)
t.start()#执行
l_p.append(t)
for t in l_p:
t.join()#同时结束
print(f"实际为",g_n)