GIL 与并发
GIL 是 Python 并发面试的必考题。主线三步:GIL 是什么、影响什么、怎么绕。
GIL 是什么
GIL(Global Interpreter Lock,全局解释器锁):CPython 解释器同一时刻只允许一个线程执行 Python 字节码。
为什么存在:
- CPython 的内存管理(引用计数)不是线程安全的
- 加 GIL 是简单粗暴的解法:整个解释器串行,不用给每个对象加锁
- 代价:多线程无法利用多核跑 Python 代码
多线程到底快不快
分场景,这是考点核心:
| 场景 | 多线程效果 | 原因 |
|---|---|---|
| CPU 密集(计算) | 不加速,甚至更慢 | GIL 串行执行,还有切换开销 |
| IO 密集(网络、文件) | 明显加速 | 阻塞等待时释放 GIL,其他线程执行 |
IO 操作(读写 socket、文件、sleep)会释放 GIL,所以 IO 密集任务多线程有效。CPU 密集任务多线程反而因锁竞争和切换变慢。
三模型选型
并发三模型各管一段
| 维度 | 线程 | 进程 | 协程 |
|---|---|---|---|
| 调度 | 操作系统抢占式 | 操作系统 | 事件循环协作式 |
| 并行 | 受 GIL 限制 | 真并行(多核) | 单线程内并发 |
| 适用 | IO 密集 | CPU 密集 | 高并发 IO |
| 开销 | 中 | 大(内存隔离) | 小 |
| 通信 | 共享内存(要锁) | IPC/队列 | 同线程共享 |
选型一句话:IO 密集用协程或线程,CPU 密集用多进程。高并发网络服务(Web/爬虫)默认协程;计算密集(数据处理)用 multiprocessing 或 Pool。
绕过 GIL 的路径
- 多进程:每个进程独立解释器、独立 GIL,多核真并行
- C 扩展:在 C 层释放 GIL(
Py_BEGIN_ALLOW_THREADS),如 numpy 的部分操作 - 其他实现:PyPy 也有 GIL;Jython/IronPython 没有但已边缘化
- 3.13 实验特性:free-threaded build(无 GIL 构建),仍在演进,生产慎用
工程结论:别指望 Python 多线程跑满多核。CPU 密集任务上多进程或把重计算下沉到 C 库(numpy/pandas 底层已释放 GIL)。
面试追问
- GIL 为什么存在? 引用计数线程安全问题的简单解法:解释器全局加锁。牺牲多核并行换实现简单
- 多线程什么时候有用? IO 密集:阻塞等待释放 GIL,其他线程趁机执行。CPU 密集无效甚至更慢
- 线程、进程、协程怎么选? IO 密集协程/线程,CPU 密集多进程。高并发网络服务用协程
- 怎么绕过 GIL? 多进程、C 扩展释放 GIL、3.13 free-threading 实验特性
- GIL 会移除吗? 3.13 提供 free-threaded 构建,但默认仍有 GIL。移除影响面太大,渐进演进