「Python 要去掉 GIL 了」这句话,从 2023 年 PEP 703 通过起就被反复传播。但真去翻官方文档会发现两件事:GIL 从来没有被移除,默认构建至今仍带它 ;而 3.13 那个「无 GIL 版本」是一个独立的实验性构建,需要单独安装。这篇按官方口径把进展、代价和几个被讲反的结论捋一遍。
GIL 到底是什么,它保护了什么?
官方的定义很精确:GIL 是 CPython 用来保证同一时刻只有一个线程执行字节码 的机制。它让对象模型(包括 dict 这类内建类型)隐式地对并发访问安全,代价是牺牲了多处理器带来的大部分并行能力。
至于为什么需要它,权威解释在 C 接口文档里:解释器本身不是完全线程安全的。原文举了一个很具体的例子------两个线程同时给同一个对象增加引用计数,计数可能只被加了一次而不是两次。所以规则是:只有持有 GIL 的线程才能操作 Python 对象或调用 Python 的 C 接口。
同一份文档还补了两句很重要的例外:解释器会定期尝试切换线程;锁在可能阻塞的 I/O 操作期间会被释放,好让其他线程有机会运行。官方术语里的 GIL 定义也强调,部分标准库与第三方扩展模块在做压缩、哈希这类计算密集任务时会主动释放 GIL。
「GIL 保证线程安全」这句话错在哪?
这是流传最广的一句错话。官方 FAQ 写得很清楚:每条字节码指令、以及由它触达的所有 C 实现代码,从 Python 程序的角度看是原子的。注意范围------是「单条指令」,不是「一段代码」。
官方还直接列了清单。被称为原子的操作包括 L.append(x)、x = L[i]、x = y、D[x] = y、L.sort() 这些;而非原子 的包括 i = i+1、L[i] = L[j]、D[x] = D[x] + 1,甚至 L.append(L[-1])。
换句话说,那个「自增不是线程安全的」经典结论在 CPython 里依然成立,+= 也一样。官方给的建议只有一句:拿不准就用锁。
PEP 703 的三阶段,现在走到第几步?
先把这份 PEP 的元信息对齐。它的标题是「在 CPython 中让全局解释器锁变为可选」,作者是 Sam Gross,状态现在是 Final ------注意它最初是 Accepted(2023 年 10 月 24 日指导委员会决议通过),后来因为成为历史文档而更新为 Final,一些镜像站还停留在旧状态上。
PEP 里提的 --disable-gil 构建选项,是为了让 CPython 能在没有 GIL 的情况下运行 Python 代码。指导委员会通过时附加了明确的保留条件:推广必须渐进、尽量少破坏,并且允许回滚。
官方把推广分成三步:
| 阶段 | 状态 | 含义 |
|---|---|---|
| 第一阶段 | 已开始(3.13) | 实验性构建,明确不用于生产,可回滚 |
| 第二阶段 | 已达成(3.14) | 官方支持但仍非默认 |
| 第三阶段 | 尚未发生 | 成为默认构建 |
| 默认构建 | 始终带 GIL | 官方下载版本至今仍使用 GIL |
第三阶段官方没有给时间表。相关 PEP 的原话是:让它成为默认(第三阶段)这件事,留给未来的 PEP 决定。
3.13 和 3.14 分别做了什么?
3.13 的动作是把第一阶段落地。官方发布说明的措辞是「CPython 现在有了实验性 的无 GIL 运行支持」,并且明确「不会默认启用 」。它需要一个单独的可执行文件,通常叫 python3.13t;也可以用构建选项从源码编译出来。
官方还专门打了两句预防针:这个模式是实验性的,工作仍在推进,要做好遇到 bug 和明显单线程性能损失的准备 。同一年官方就提供了 sys._is_gil_enabled() 这个函数,用来在运行时判断 GIL 到底有没有被关掉。
3.14 的动作是把它从「实验」推进到「官方支持」。官方发布说明写的是:PEP 703 描述的实现在 3.14 中完成,包括 C 接口的改动;单线程代码的性能损失现在约为 5% 到 10%,具体取决于平台和 C 编译器。与之配套的是一份新 PEP,为「支持状态」设定了明确标准,它的状态也是 Final,决议时间在 2025 年 6 月。
需要强调的是,「官方支持」不等于「可以无脑上生产」。官方把它定位为可选、非默认 的构建,需要按需评估;官方也没有发布过「建议生产环境默认启用」的表述。
打开 free-threading 有什么代价?
三个代价都在官方文档里有明确数字或明确结构。
第一是单线程回退 。这个数字在官方内部就存在版本差异:一份 PEP 里写「大约 10%,macOS 上大约 3%」,3.14 的发布说明写「5% 到 10%」,最新的官方指南写「从 macOS aarch64 上约 1% 到 x86-64 Linux 上约 8%」。引用时必须带上版本号和平台,否则就是个没意义的数字。
第二是内存上涨 。官方给出的是「约 15% 到 20% 更高的内存占用」,并把它作为第二阶段要压到 20% 以内的目标。至于社区常说的「对象头从 16 字节涨到约 32 字节」------官方只给出了结构体的字段与各自的字节范围,没有给实测总增量,这个数字属于社区来源。
第三是C 扩展的兼容性 。官方要求扩展模块显式声明自己支持无 GIL 运行,否则导入时会告警并在运行时把 GIL 重新打开。这个提醒很重要:即使你装的是 free-threaded 构建,只要导入了一个没标记的 C 扩展,GIL 就可能被悄悄恢复。
也正因如此,官方特别说明 free-threaded 构建目前不支持受限 C 接口和稳定 ABI。
怎么判断当前解释器带不带 GIL?
不用猜,官方给了两个现成的接口。
python
import sys
import sysconfig
# 是否 free-threaded 构建:1 表示是
print(sysconfig.get_config_var("Py_GIL_DISABLED"))
# 运行时 GIL 当前是否开启:True 表示 GIL 开着
print(sys._is_gil_enabled())
这两个结果可能不一致,原因就是上面那条------导入未标记的 C 扩展会把 GIL 重新打开。官方明确推荐用前一个接口来判断构建配置。
想在运行时控制 GIL 开关,可以借助环境变量或命令行选项。free-threaded 构建默认关闭 GIL,可以重新打开,也可以强制关闭。
bash
# 强制关闭 GIL 运行
PYTHON_GIL=0 python3.13t script.py
# 等价的命令行写法
python3.13t -X gil=0 script.py
顺便说清一个常被引用的参数:sys.setswitchinterval 决定给并发线程分配的时间片 的理想长度,官方说明「实际值可能更高」且「哪个线程被调度是操作系统的决定,解释器没有自己的调度器」。它的默认值是 5 毫秒,这个数字的权威出处是 3.2 版本的发布说明,而不是函数文档本身。
几处常见说法,哪些是错的
第一,「3.13 就去掉 GIL 了」。 3.13 提供的是一个实验性构建,默认安装不带它。
第二,「GIL 已被移除」。 官方两次白纸黑字:GIL 仍将是 CPython 构建和官方下载版本的默认选项。
第三,「GIL 保证了线程安全」。 它只保证单条字节码原子,+= 和自增都不是原子的。
第四,「多线程对 I/O 密集也没用」。 相反,I/O 期间 GIL 会被释放,所以 I/O 密集场景下多线程仍然有效。
第五,「绕过 GIL 只能靠多进程」。 官方列出的办法至少还有三种:释放 GIL 的 C 扩展、子解释器,以及 free-threading 构建。
第六,「换成 free-threading 所有代码都会变快」。 官方说的是「并非所有软件都能自动受益」,且单线程反而会更慢,只有为多线程设计、核数又够的场景才有收益。至于社区里流传的各种「加速 N 倍」,官方没有给过统一的加速比数字。
把这件事记成一句:去 GIL 是一个已经走到「官方支持」但仍在「可选、非默认」的位置上的长期工程。它在进步,但离「默认无 GIL」还有明确的一段距离,而官方明确表示那一步留给未来决定。判断要不要现在就上,关键看你的瓶颈是不是卡在 CPU 密集的纯 Python 多线程上------如果是,收益真实;如果不是,单线程那 5% 到 10% 的回退是立刻要付的账。