Python 3.14 Free-Threaded 生产可用:GIL 40 年锁链终于解开
Python 多线程的「锁」终于被解开了。
2025 年 10 月,Python 3.14 正式发布,free-threaded build 从实验性转为官方支持。Python Steering Council 接受 PEP 779,这意味着 CPython 第一次在生产环境里能跑出真正的 CPU 级多线程并行。GIL 这个困扰了 40 年的设计,从可选变成了官方推荐路径之一。
一、40 年的 GIL 是什么
GIL 全称 Global Interpreter Lock,是 CPython 最早期的设计选择。它让引用计数(reference counting)天然线程安全——代价是 CPU 密集型多线程完全失去并行能力。
工作负载分两类:
- I/O 密集型(Web 服务器、网络客户端):GIL 没事,I/O 时 Python 主动释放锁
- CPU 密集型(数据处理、数值计算、图像处理):线程并行度是 0,必须用
multiprocessing绕开
过去 40 年所有想干掉 GIL 的尝试都失败了。2016 年 Larry Hastings 的 “GIL-ectomy” 工作证明可行,但单线程性能回退 30-40%。社区拒绝。
二、Sam Gross 的破局
2021 年,Meta 工程师 Sam Gross 公开了一个 GIL-removed 的 CPython fork,用了三招:
- Biased reference counting(偏向引用计数)
- Deferred reference counting(延迟引用计数)
- 细粒度 lock-free 数据结构
这个 fork 推动了 PEP 703(2023 年 1 月提交),并制定了一个三阶段落地计划:
|| 阶段 | Python 版本 | 状态 | 说明 |
||------|------------|------|------|
|| Phase I | 3.13 (2024 年 10 月) | 完成 | 实验性 free-threaded build,需 --disable-gil 启用 |
|| Phase II | 3.14 (2025 年 10 月) | 当前 | 正式支持,仍然可选 |
|| Phase III | 未来 | 计划中 | free-threaded build 成为默认 |
Meta 出工程师,Quansight Labs 负责社区协调。这是大厂和开源社区罕见的高质量协作。
三、3.14 相对 3.13 的关键改进
Python 3.13 的 free-threaded build 还是实验性,单线程开销 ~40%。3.14 这次终于能用了:
- 单线程开销从 40% 降到 5-10%——PEP 659 的 specializing adaptive interpreter 在 free-threaded 模式被重新启用
- 在 pyperformance 基准套件上:开销范围从 macOS aarch64 上的 ~1% 到 x86-64 Linux 上的 8%
- PEP 703 实现完整——所有 C API 改动收尾,临时方案换成永久方案
- PEP 779 2025 年 6 月通过——free-threaded build 摘掉”实验性”标签
这是从「能跑」到「能用」的本质跨越。
四、性能基准:数字不会骗人
下面这张表是 free-threaded 模式最值得看的对比数据:
|| 基准测试 | 标准 Python | Free-Threaded(4 线程)| 加速比 | ||----------|-------------|---------------------|--------| || 质数计算 | 3.70s | 0.35s | ~10x | || 斐波那契(4 线程)| 25-33s | 9.3s | ~3x | || 文件 I/O(20 文件,线程池)| 18.77s | 5.13s | ~3.6x | || 矩阵乘法(multiprocessing 基线)| 4.49s | 6.29s | 0.7x(变慢)|
注意最后一行:free-threaded Python 不是对所有 CPU 密集任务都更快。已经在用 multiprocessing 优化的任务,切换到 free-threaded 模式可能变慢——因为细粒度对象锁的开销替换了粗粒度的 GIL。
Free-threaded Python 的甜点场景
- 多个 CPU 密集任务并行运行
- 任务之间不重度共享可变状态
- 以前被
multiprocessingIPC 开销拖死的负载 - 需要在并行任务之间共享内存访问
Django 基准测试:一份用 free-threaded Python 3.14 的 Django 基准显示 ASGI 请求处理吞吐量提升 ~2 倍。
五、生态准备度:51% 临界点
截至 2026 年初,PyPI 上下载量最高的 360 个有原生 wheel 的包中,183 个发布了 free-threaded wheel——约 51% 兼容性。一半的门槛过了。
| 包 | 状态 | 备注 |
|---|---|---|
| NumPy | 2.1 起支持 | 仍有一些线程瓶颈在解决 |
| SciPy | 进行中 | 扩展模块移植中 |
| pandas | 进行中 | 工作持续 |
| scikit-learn | 进行中 | Quansight 主导 |
| Matplotlib | 进行中 | — |
可以跟踪的兼容性列表:
- 跟踪表:https://py-free-threading.github.io/tracking/
- Wheel 索引:https://hugovk.github.io/free-threaded-wheels/
关键软件包兼容后,free-threaded 模式才会从「能跑基准」变成「能跑生产」。
六、落地实战:识别与切换
6.1 识别 free-threaded Python
三种方式,从轻到重:
# 快速检查import sysprint(sys.version) # 包含 "free-threading build" 字样
# 运行期 GIL 状态print(sys._is_gil_enabled()) # True/False
# 构建配置(推荐用于构建判断)import sysconfigprint(sysconfig.get_config_var("Py_GIL_DISABLED")) # 1 = 支持推荐用 sysconfig.get_config_var("Py_GIL_DISABLED")——这是构建期判断,不依赖运行期状态。
6.2 安装 free-threaded Python
- macOS 与 Windows:官方安装包(https://www.python.org/downloads/)可选 free-threaded 二进制
- 其他平台:见社区维护的安装指南(https://py-free-threading.github.io/installing-cpython/)
- 从源码构建:
./configure --disable-gil
6.3 启用 GIL 的三种方式
free-threaded build 可选回 GIL:
# 环境变量export PYTHON_GIL=1
# 命令行python -X gil script.py如果导入了一个 C 扩展模块没声明支持 free threading,GIL 会自动重新启用并打印警告。这是兼容性兜底。
七、必须知道的限制
7.1 已知问题清单
Immortalization
部分对象是 immortal(永生)—— 不释放,永不修改引用计数(避免引用计数竞争)。3.14 的 immortal 范围:
- 代码常量(数字/字符串字面量、常量 tuple 字面量)
sys.intern()intern 过的字符串
Frame 对象
# ❌ 危险——可能直接 crash 解释器frame.f_locals # 另一个线程正在执行这个 frame 时迭代器
# ❌ 不是线程安全——可能重复或漏元素for item in shared_iterator: # 多个线程并发 ...7.2 内存使用上升
free-threaded build 内存占用更高,原因有三:
1. 所有 intern 字符串都是 immortal
Python ≥ 2.3 时代 intern 字符串可以从 intern 表移除,free-threaded 模式下 所有 intern 字符串都是 immortal——直到解释器关闭才释放。
2. 非 GC 对象有更大的对象头
GC info 现在是对象头的一部分,而不是在 PyObject 之前分配。
None 在 free-threaded build 占 32 字节,默认 build 占 16 字节(AMD64)GC 对象(dict、list)两种 build 体积相同3. QSBR 延迟释放
用 Quiescent State-Based Reclamation (QSBR) 实现 lock-free 数据结构(list 对象、dict 键对象等),释放操作被延迟。
# 显式触发 QSBR 释放import gcgc.collect() # QSBR 持有的内存会释放7.3 行为变化
sys.flags.thread_inherit_context默认 true——threading.Thread创建的线程从调用者Context()复制(默认 build 中为空)sys.flags.context_aware_warnings默认 true——warnings.catch_warnings用 context variable 存 warning filters
八、生产落地的三个判断
第一,先量后切
不是所有服务都该切到 free-threaded。multiprocessing 已经在用的、CPU 单核够用的、I/O 密集的——这些切过去要么没收益要么会变慢。先用 multiprocessing 之外的多线程场景试。
第二,看依赖链
Django 框架、NumPy、SQLAlchemy 这类底层库的支持状态决定上层应用能不能切。51% 兼容率是个平均值,你的项目实际兼容率可能 30% 也可能 80%。先查跟踪表(https://py-free-threading.github.io/tracking/)。
第三,关注 C 扩展
自定义 C 扩展模块不在 wheel 列表里——需要手动声明 free-threading 支持。这是迁移最容易踩坑的地方。没标过的 C 扩展导入时会自动启用 GIL 并打 warning,等于”白切”。
九、最后
GIL 不是被「干掉」的,是被「替换」的。
40 年的粗粒度全局锁,换成细粒度的对象级锁。tradeoff 是单线程性能 5-10% 的回退,换多线程下 3-10 倍的 CPU 利用率。这是个划算的买卖,至少对 CPU 密集服务来说。
Python 3.14 不是终点。Phase III 计划让 free-threaded 成为默认——那是真正的「GIL 死亡」时刻。但 2026 年的工程现实是:51% 生态兼容、5-10% 单线程回退、10 倍 CPU 加速——这是一组值得认真评估的数字。
不要因为「Python 多线程没用」就让 2024 年的架构躺到 2027 年。那个判断在 2025 年 10 月已经失效了。
← Back to blog