Python 的 GIL 是什么,它对多线程有什么影响?
面试官抛出 GIL,其实是想看你有没有在真实场景里被它“坑”过,以及是否知道如何绕开它。我先从一次线上故障讲起,再拆原理和影响,最后落到怎么选型。
🔸 一句话讲清 GIL 是什么
GIL(Global Interpreter Lock)是 CPython 解释器里的一把全局互斥锁,它规定:同一时刻,只有一个线程可以执行 Python 字节码。哪怕你有 16 核 CPU,多线程也只能在单核上轮转,根本做不到真正的并行计算。
⚙️ 为什么 CPython 非要搞这把锁?
根本原因是为了内存管理的线程安全。
CPython 内部用引用计数管理对象生命周期,比如 a = [] 这个对象的引用计数就 +1。
如果多个线程同时修改同一个对象的引用计数,没有锁就会发生竞争,导致内存泄漏或对象提前释放。
最简单的解法就是加一把全局大锁,让任何线程操作 Python 对象之前都必须先拿到 GIL。代价就是多线程变成“并发”而不是“并行”。
📌 对多线程的具体影响,分三种场景说
1️⃣ CPU 密集型任务:多线程反而更慢¶
我们有一次在 API 里用多线程做图片哈希计算(纯 Python 循环),4 核机器上开 4 个线程,理论应该快 4 倍,结果反而比单线程慢了 15%。
原因:
-
线程频繁争抢 GIL,上下文切换开销巨大。
-
操作系统以为线程在工作,实际 CPU 时间大量消耗在锁竞争和唤醒上。
结论:CPU 密集必须用
multiprocessing多进程,或 C 扩展(Cython/Numba)释放 GIL。
2️⃣ I/O 密集型任务:多线程很合适¶
当线程在等待网络响应、磁盘读写时,它会主动释放 GIL(比如在 socket.recv 等 C 函数内部)。
这时其他线程可以立即获得 GIL 开始工作,所以 I/O 密集场景下,多线程能显著提升吞吐。
我们自研的爬虫调度器,用 ThreadPoolExecutor 做并发下载,10 个线程跑满带宽,GIL 几乎不是瓶颈。
3️⃣ 混合型任务:需要实测,不能想当然¶
我们做过一个报表生成服务,前半部分是数据库查询(I/O 密集),后半部分是 JSON 序列化和压缩(CPU 密集)。
最终方案是:线程池做并发查询,返回数据后交给进程池做序列化,中间用 Queue 传递数据,这样 GIL 对 CPU 部分的影响被多进程绕开了。