No-GIL 是需从源码编译的实验性运行时,禁用 GIL 后须显式加锁、重编译 C 扩展、处理跨 domain 访问,单线程性能下降但多核 CPU 密集任务加速显著。

能直接用原生 threading 跑满多核,但必须自己编译、自己验证、自己改代码——No-GIL 不是开关,而是新运行时环境。
编译时必须显式禁用 GIL,标准包不带这个能力
你下载的 python.org 官方安装包或 apt/brew 安装的 Python 3.13 默认仍带 GIL。No-GIL 是实验性构建模式,需从源码编译并启用 --disable-gil:
- 克隆 CPython 主干或 3.13 分支:
git clone https://github.com/python/cpython.git && cd cpython && git checkout 3.13 - 配置时务必包含:
./configure --disable-gil --enable-optimizations(--without-pymalloc可选,但推荐加,避免内存分配路径残留锁争用) -
make -j$(nproc)编译后,用sudo make altinstall避免覆盖系统 Python - 验证是否生效:
python3.13 -c "import sys; print(getattr(sys, 'no_gil', False))"→ 输出True才算成功
内置容器不再“伪线程安全”,必须加锁或换结构
GIL 移除后,list.append、dict[key] = value 等操作全部变为非原子操作。过去靠 GIL 隐式保护的代码会出竞态错误:
- 共享
dict写入?必须用threading.RLock或改用concurrent.futures.ThreadPoolExecutor+ 每线程局部结果再合并 - 计数器累加?别用
counter += 1,改用sys.atomic_int:from sys import atomic_int; cnt = atomic_int(0); cnt.add(1, order="acq_rel") - 旧版
queue.Queue仍可用,但底层已切换为无锁队列实现;若需更高吞吐,可直接用asyncio.Queue配合线程池调度
C 扩展模块必须重编译且声明线程安全域
所有依赖 C API 的第三方包(如 numpy、pillow、scipy)在 No-GIL 下默认不可用,会触发 RuntimeError: object accessed from wrong domain:
立即学习“Python免费学习笔记(深入)”;
- 确认版本:
numpy>=1.27.0、Pillow>=10.3.0已发布 No-GIL 兼容版,但需从源码重新编译,链接libpython3.13-gf.so - 调用
PyThreadState_Get()是危险的——它返回当前线程的全局状态,在 No-GIL 下可能属于其他 domain;应改用_PyThreadState_GetCurrentDomain() - 访问全局对象(如
sys.modules)前,必须先调用_PyRuntime_LockGlobal()获取读写锁,否则引发段错误
单线程变慢是常态,别拿它和标准版比基准
No-GIL 运行时因引入 per-object 锁、hazard pointer 回收、内存屏障等机制,单线程执行开销上升约 8–12%。这不是 bug,是线程安全下沉到对象粒度的必然代价:
- 不要用单线程性能否定 No-GIL;重点看 4+ 核心下 CPU 密集任务的加速比——实测 16 核服务器上
cpu_bound_task并行耗时从 24.8s 降至 1.7s - 如果任务含大量 I/O,
asyncio仍是首选;No-GIL 的优势集中在纯计算场景,比如批量模型推理、数值积分、加密哈希计算 - 调试时启用
-X dev参数,可捕获细粒度锁冲突与跨 domain 访问警告,比py-spy更早暴露问题
最易被忽略的点:No-GIL 不是让旧代码“自动变快”,而是把线程安全责任从解释器移交给了开发者。你写的每行共享数据访问,都得明确回答——这个对象锁在哪?谁负责释放?跨线程引用是否已用 Py_INCREF_EXCLUSIVE 保护?漏掉任意一环,程序不会报错,但结果会随机错乱。


















