GIL是CPython为简化引用计数内存管理而设的全局互斥锁,牺牲多线程CPU并行性以保障C扩展兼容与实现简单;Python 3.12仅优化为细粒度锁,未移除GIL。

为什么 CPython 要加 GIL
GIL(Global Interpreter Lock)不是 Python 语言规范的一部分,而是 CPython 解释器实现层面的互斥锁。它的存在主要为了简化内存管理——CPython 的垃圾回收基于引用计数,而引用计数的增减操作必须是原子的。如果没有 GIL,多线程并发修改同一对象的 ob_refcnt 字段会导致计数错误、内存泄漏甚至崩溃。
换句话说,GIL 是用「牺牲并发性」换来了「实现简单性」和「C 扩展兼容性」。它让大量依赖 C API 的第三方库(如 numpy、cv2)无需额外加锁就能安全运行。
GIL 对多线程 CPU 密集型任务的实际影响
在纯计算场景下,GIL 会让多线程几乎无法并行利用多核:
-
threading.Thread启动 4 个计算函数,实际仍是轮转执行,总耗时接近单线程 × 4 -
time.sleep()或文件 I/O 等阻塞操作会主动释放 GIL,此时其他线程可抢占,所以多线程对 I/O 密集型任务仍有意义 -
numpy、scipy等底层用 C 实现的运算,通常会在执行前释放 GIL,因此多线程调用它们能真正并行
验证方式很简单:写一个死循环累加的函数,用 threading 和 multiprocessing 分别跑 4 次,对比 time.time() 差值——前者基本不提速,后者接近 4 倍加速。
立即学习“Python免费学习笔记(深入)”;
绕过 GIL 的常见路径与代价
真要并行 CPU 密集任务,主流做法只有三个,各自有明确取舍:
- 用
multiprocessing:每个进程有独立解释器和内存空间,天然绕过 GIL;但进程启动开销大、进程间通信(Queue、Pipe)比线程共享变量慢得多,且无法直接传 lambda 或闭包 - 用
asyncio+ 协程:只适用于 I/O 密集场景,对 CPU 密集无效,因为协程仍运行在单线程中,不释放 GIL - 换解释器:比如
PyPy(部分场景无 GIL,但不保证)、Jython(JVM 上无 GIL)、Cython编译关键函数为 C 扩展并在内部释放 GIL——但开发成本陡增,调试难度上升
注意:concurrent.futures.ThreadPoolExecutor 和 ProcessPoolExecutor 的区别,本质上就是上面两条路的封装,选错池子等于白忙活。
GIL 在 Python 3.12 中的改进与局限
Python 3.12 引入了「细粒度锁」机制,把原先一把大锁拆成多个更小的锁(如针对对象分配、GC、字节码执行等),理论上提升了多线程协作效率。但官方明确说明:这不等于移除 GIL,CPU 密集型纯 Python 代码依然无法并行。
实际效果取决于 workload 类型:如果线程频繁切换、做大量小对象创建/销毁,3.12 可能比 3.11 快 10% 左右;但如果主线程一直在跑 for i in range(10**8): x += i,其他线程依然拿不到 GIL。
真正想靠语言升级解决 GIL 问题,得等到「免 GIL 构建选项」稳定落地——目前还只是实验性编译开关(--without-pygil),连 alpha 都不算。









