GIL本质是CPython解释器的一把互斥锁,强制同一时刻仅一个线程执行Python字节码,旨在保障引用计数内存安全及兼容C扩展;它导致CPU密集型多线程无法并行,而IO密集型因系统调用主动释放GIL仍可高效并发。

GIL 本质是 CPython 解释器的一把互斥锁,它强制同一时刻只有一个线程能执行 Python 字节码。 这不是 bug,也不是设计疏忽,而是 CPython 为保障内存安全(尤其是引用计数)和兼容大量 C 扩展所做出的工程取舍。它直接导致多线程在 CPU 密集场景下无法并行,甚至比单线程更慢。
为什么 threading 在 CPU 密集任务里跑不满多核?
因为 GIL 不按函数、语句或时间片释放,而是按字节码指令或固定时间间隔(Python 3.12+ 默认每 0.005 秒)强制切换。哪怕你启了 8 个 threading.Thread 做矩阵运算,所有线程都在争抢同一把锁:
- 每个线程执行几条字节码后就得交出
GIL,然后排队等待重新获取 - 线程上下文切换 + 锁竞争本身消耗 CPU,而计算没提速
-
top或htop显示始终只有一个核心满载,其余闲置
为什么 threading 在 IO 密集任务里还能用?
因为 IO 操作(如 socket.recv()、open().read()、requests.get())会主动释放 GIL —— 线程挂起等待网络响应或磁盘读取时,其他线程立刻能拿到锁继续干活:
- 这不是 GIL “变松”了,而是解释器在进入系统调用前显式调用
PyThreadState_Release() - 所以 10 个线程发 HTTP 请求,可能总耗时只比 1 个略多一点,而非 10 倍
- 但一旦请求返回后开始解析 JSON 或做字符串处理,GIL 又锁死,计算部分仍串行
为什么 numpy 多线程计算不卡?
因为像 numpy.dot()、pandas.groupby() 这类底层用 C/Fortran 实现的函数,在执行密集计算时会手动释放 GIL,让其他 Python 线程得以并行运行:
立即学习“Python免费学习笔记(深入)”;
- 这是 C 扩展开发者显式调用
Py_BEGIN_ALLOW_THREADS/Py_END_ALLOW_THREADS实现的 - 你不能指望普通 Python 循环(比如
for i in range(N): total += i*i)自动释放 GIL - 如果你自己写 C 扩展,也必须主动管理 GIL,否则照样被锁住
真正容易被忽略的是:GIL 并不保证你的代码线程安全。哪怕只有一行 x += 1,它对应至少 4 条字节码,多线程下依然可能丢数据 —— 该加 threading.Lock() 还得加。



















