GIL导致多线程在CPU密集任务中本质串行,因纯计算不触发GIL释放,仅靠ticks被动切换,引发频繁锁竞争、线程切换开销及缓存失效,实测常比单线程慢10%–30%;multiprocessing能绕过GIL,但有进程启动和通信成本。

因为CPython解释器存在GIL(全局解释器锁),它强制同一时刻只有一个线程执行Python字节码——数值计算几乎不触发GIL释放,所以多线程在CPU密集场景下本质是串行的。
为什么threading跑纯循环反而比单线程慢?
纯数值计算(如累加、幂运算、矩阵元素遍历)不涉及I/O或系统调用,GIL不会被主动让出;解释器只靠“ticks计数”(默认100次字节码)被动释放GIL。这导致:
- 多个线程反复争抢同一把锁,线程切换开销显著增加
- CPU缓存频繁失效,核心间数据同步成本上升
- 实测中4线程跑
sum(range(50_000_000))常比单线程慢10%~30%
multiprocessing真能绕过GIL吗?
能,但要注意进程启动和通信成本:
- 每个
Process拥有独立内存空间和独立GIL,天然并行 - 传参需序列化(
pickle),大数据量时Queue或Pipe可能成瓶颈 - 小任务(如每次计算
- 推荐用
concurrent.futures.ProcessPoolExecutor替代裸multiprocessing.Process,自动管理池与结果
NumPy运算为啥能跑满多核?
它不依赖Python字节码执行,而是在C/Fortran层调用BLAS库(如OpenBLAS、MKL):
立即学习“Python免费学习笔记(深入)”;
- 这些底层库内部实现真正的多线程,且运行时不受GIL约束
- 但必须显式配置线程数:
export OMP_NUM_THREADS=4或os.environ["OMP_NUM_THREADS"] = "4" - 若未设置,多数预编译NumPy默认只用1个线程——你看到的“单核跑满”其实是BLAS单线程在满载
-
torch.set_num_threads(4)对PyTorch CPU运算有效,但仅控制其内部线程,不影响底层BLAS
GIL不是bug,是CPython为简化引用计数内存管理做的取舍;真正要写高性能数值计算,得习惯避开threading、善用multiprocessing或转入NumPy/SciPy/Cython生态——否则再多核也白搭。


















