先说个技术群里经常出现的画面:有人贴出一段Python多线程代码,开了8个线程做纯计算,然后发截图问“为什么CPU占用率只有12%,其他核都在睡觉?是不是GIL把这程序废了?”底下评论立刻分成两派,一派说“Python多线程就是个笑话”,另一派说“你们根本不会用多线程”。作为两种方案都长期用过的人,我一直觉得这个争论有点跑偏——GIL确实是Python多线程绕不开的限制,但它锁死的场景没有网上传的那么宽。选多线程还是多进程,本质不是态度问题,也不是“哪个更高级”的问题,而是任务类型问题。这篇文章就把GIL的机制、多线程和多进程各自适用的场景、以及我自己的选型判断路径一次说清楚,适合刚接触并发编程的初学者,也适合写过并发但总在调优时折返的老手。
1. 先给结论:别再被GIL吓倒,选型只看任务类型
1.1 30秒速查表
很多文章聊到GIL就收不住了,从CPython源码讲到操作系统调度,读者看完依然不知道明天写代码时该用哪个。我这里直接给一张速查表,你先拿去用,后面再细讲原理。
| 任务类型 | 推荐方案 | 核心原因 |
|---|---|---|
| CPU密集(大量纯Python计算、循环、递归) | 多进程(ProcessPoolExecutor或multiprocessing) | 每个进程独立解释器、各持一把GIL,真正利用多核 |
| I/O密集(网络请求、文件读写、数据库查询) | 多线程(ThreadPoolExecutor或threading) | 等待I/O时线程会释放GIL,线程开销远小于进程 |
| 计算发生在C扩展内部(numpy、pandas、opencv) | 多线程(甚至更优) | 底层C库在运行时会显式释放GIL,多线程可并行 |
| 混合型(计算+I/O同时存在) | 按阶段拆分 | 计算段用多进程,I/O段用多线程,或者用进程内再开线程池的混合模型 |
这张表看起来很简单,但它是我在实际项目中反复验证过的结论。接下来要解决的唯一问题是:为什么结论偏偏是这样?这就要从GIL这个“历史包袱”说起。
1.2 简单说,“多线程没用”是一种误读
我见过不少从Java转过来的开发,第一反应是:Java里线程能跑满多核,怎么Python就多线程就不行了?然后得出“Python并发很弱”的结论。这个结论错就错在范围感。GIL限制的是“同一个进程内、多个线程同时执行Python字节码”,它没有限制你开多个进程,也没有限制你在等待外部资源时让线程切换。
换个生活化的说法:多线程就像一间厨房里只有一个灶台,你可以安排十个帮厨,但同一时刻只有一个帮厨能站在灶台前做菜,其他帮厨等得再急也插不上手。多进程就好比你直接多租了几间厨房,每间厨房都有自己的灶台,几个厨师能同时开工。但代价是——多租厨房要花钱,多起进程要耗内存,厨房之间递菜还要经过一个传菜口(进程间通信)。
这个类比能解释绝大多数问题:为什么CPU密集任务用多线程没用?因为只有一个灶台,帮厨再多也轮不上炒菜,反而站在那里挤来挤去。为什么网络请求用多线程有用?因为帮厨把菜炖上之后干等着(等网络响应的这段时间),灶台就空出来了,下一个帮厨可以上去起火开炒。为什么numpy用多线程有时更快?因为numpy的C扩展在炒菜时主动把灶台让开,让其他人也能用。
需要模型API调用? 免费领10W Token,多模型网关一键接入 Claude、DeepSeek 等主流模型。
2. GIL到底锁住了什么:理解它才能理解整个选择
2.1 锁住的是“Python字节码的执行权”,不是全局死锁
GIL全称Global Interpreter Lock,很多人误以为它会把整个程序锁死,或者认为Python程序一次只能做一件事。准确地说,GIL是CPython解释器里的一个互斥锁,它保证同一个进程内,任意时刻只有一个线程在执行Python字节码。这个“字节码”指的是解释器执行的那层指令,比如 LOAD_FAST、BINARY_ADD 这些。
那Python为什么要给自己套这么一层枷锁?核心原因在内存管理。CPython对对象采用引用计数:每个对象内部有一个计数器,记录有多少个变量引用它,计数归零就立即回收内存。如果两个线程同时去修改同一个对象的引用计数,计数的加减不是原子的,就可能导致一个对象的计数器错过一次减一,永远不被回收,这就是内存泄露;更严重的是计数器先于对象真实销毁变成0,对象被提前回收,另一个线程还在用,直接崩溃。
解决方案有两种:一种是给每个对象或者每个数据结构都加细粒度锁,写起来安全但性能成本高,而且锁的粒度越细,死锁的可能性越复杂;另一种就是GIL,给整个解释器加一把大锁,用极简的思路换线程安全。在上个世纪90年代,Python的核心场景是单核机器,GIL这种方式简单、稳定、性能可接受,所以一直沿用下来。
2.2 GIL的运行规则:什么时候释放,什么时候持有
GIL机制在实践里最重要的就三条规则:
