GIL 全局解释器锁深度
理解 GIL 的本质、影响和绕过方案
- 理解 GIL 是什么以及为什么存在
- 理解 GIL 对多线程性能的影响
- 掌握绕过 GIL 的方案
- 了解 PEP 703 无 GIL 进展
GIL 全局解释器锁深度
GIL(Global Interpreter Lock)是 Python 最著名也最容易被误解的机制。理解 GIL 是写出正确并发代码的前提。
GIL 是什么?
GIL 是 CPython 解释器里的互斥锁,同一时刻只能有一个线程执行 Python 字节码。就算用多线程,Python 代码也没法真正并行。
GIL 不是 bug,是设计取舍。它让 CPython 的内存管理(引用计数)变得简单快速,C 扩展集成也更容易。代价是 CPU 密集多线程无法利用多核。注意:I/O 密集任务(网络请求、文件读写)多线程仍然有效,因为等待 I/O 时会释放 GIL。
GIL 什么时候释放?
绕过 GIL 的方案
PEP 703:无 GIL 的未来
Python 3.13 引入了实验性的 free-threaded 构建(--disable-gil),用 biased reference counting 和每对象锁替代 GIL:
I/O 密集 → asyncio 或多线程;CPU 密集 → 多进程或 C 扩展;数据处理 → NumPy/Pandas(内部释放 GIL);别一上来就用多进程,进程间通信的序列化成本很高。
GIL 的 C 实现细节
GIL 在 CPython 中是一个条件变量 + 互斥锁:
// CPython 源码简化
// cpython/Python/ceval_gil.c
static struct {
PyMutex mutex;
PyCondVar cond;
int locked; // GIL 是否被持有
unsigned long interval; // 切换间隔(us)
} gil;
void take_gil(PyThreadState *tstate) {
// 如果 GIL 被持有,等待
// 每隔 interval 检查 gil_drop_request
}
void drop_gil(PyThreadState *tstate) {
// 释放 GIL,通知等待线程
}
// eval_breaker 机制:
// 每执行一定数量字节码检查是否需要释放 GIL
// 这就是 sys.setswitchinterval 控制的为什么 Python 多线程在 CPU 密集任务中没有加速?
CPU 密集任务应该用什么方案绕过 GIL?
资深工程师加餐
底层原理 · 大厂视角 · 工程经验,点卡片展开
with 语句等价于 try/finally:无论正常结束还是中途抛异常,退出时都会执行清理(关文件、释放锁、断开连接)。实现方式有两种:类里写 __enter__/__exit__,或用 contextlib.contextmanager 把生成器变成上下文管理器。凡是「打开了必须关闭/获取了必须释放」的资源,都应该用 with 托管。
挑战任务
GIL 影响分析器
对比单线程、多线程、多进程在 CPU 密集和 I/O 密集任务上的表现差异。
课后作业
并发模型选择
写一个函数,根据任务类型(cpu/io)推荐并发方案。