Python 的全局解释器锁(GIL)就是“爱恨交织”的经典案例。
虽然它简化了语言设计,实现了快速增长和稳定,但它也成为寻求利用多核 CPU 的多线程程序的瓶颈。
这就是为什么软件工程师(尤其是其他编程语言的软件工程师)会说“Python 很慢!”的主要原因。
值得庆幸的是,Python 社区发生了突破性的改变,按照 PEP 703 的提议,Python 将在几年内逐步移除 GIL。
本文将带你快速了解臭名昭著的 GIL 的过去、现在和未来。更重要的是,本文还提供了一个适合初学者的指南,教你如何在本地环境中安装和尝试无 GIL 的 Python。
快速浏览:什么是 GIL?
在设置无 GIL 的 Python 之前,我们先来快速了解一下这个臭名昭著的 GIL。它到底是什么?
| 简而言之,GIL 是一个互斥锁,确保一次只有一个线程执行 Python 字节码。 |
|---|
它于 90 年代初推出,提供了线程安全性和简单性。当时,单核 CPU 是标准配置,GIL 带来的性能问题极小。它还简化了与 C 扩展的集成,这显著提升了 Python 的采用率。
但是现在已经是 2025 年了,你在哪里可以买到单核 CPU?
为什么 GIL 会成为一个问题?
每一个选择,无论好坏,都会带来后果。
由于 GIL 这一语言设计上的大胆选择,Python 线程无法真正并行运行,这使得 CPU 密集型多线程程序不适合用 Python 编写。
由于这一限制,开发人员已转向替代解决方案,例如多处理,它创建单独的进程来利用多个 CPU 内核,以及 NumPy 或 Cython 等外部库,它们将计算密集型任务卸载到已编译的 C 扩展。
虽然这些解决方法有效地规避了线程限制,但它们带来了额外的复杂性、开销和资源消耗。
所有这些都促使 Python 社区不断要求彻底删除 GIL。
然而,正如 Python 之父 Guido van Rossum 在《移除 GIL 并不容易》一文中所写。如果移除 GIL 不会损害单线程性能或向后兼容性,他很乐意这么做,但这些限制条件很难满足。
因此,在 PEP 703 解决方案出现之前,Python 从未真正制定出一个可靠的计划来删除它。
PEP 703:美丽新世界(没有 GIL)
PEP 703诞生了,这是 Sam Gross 接受的解决方案,旨在使 GIL 成为可选项。
正如其所描述的,该提案不会立即消除 GIL,而是引入一个编译时选项(--disable-gil),这是一个新的构建配置标志,用于试验无 GIL 的 Python。
Sam Gross 的创新方法解决了之前的障碍:
- 偏向引用计数通过使用线程本地计数器来减少开销。
- 不朽对象经常使用的不可变对象(如整数和单例)永远不会改变引用计数,从而消除了不必要的锁。
- 线程安全内存管理利用现代分配器(例如mimalloc)有效地处理并发内存操作。
Python 委员会接受了 PEP 703,并计划分阶段进行谨慎的引入:
- Python 3.13(于 2024 年发布):可选的无 GIL 构建,用于测试和社区反馈(由--disable-gil构建时间标志控制)。
- 过渡阶段(~2026-2027):统一构建,实现 GIL 的运行时切换。
- 未来目标(~2028+):No-GIL Python 成为默认,完全解锁多核并行性。
如何设置 No-GIL Python
说起来容易做起来难,让我们看看如何在本地环境中真正禁用 GIL。
| 我使用 MacOS 版本 15.5 进行此设置,其他操作系统应该会看到类似的结果,但操作可能会略有不同。 |
|---|
首先,让我们从官方网站下载最新的 Python 3.13 安装程序(根据您的操作系统选择正确的版本):
打开安装程序后,我们可以看到关于GIL的令人兴奋的信息:
然后,在“安装类型”步骤中,我们需要勾选“自由线程 Python [实验]”选项(默认情况下未选中)并安装:
安装完成后,是时候执行路径中的“ Install Certificates.command ”文件/Applications/Python 3.13/来使用新 Python 下载并安装 SSL 根证书以供其使用:
至此,整个安装过程已完成。系统中安装了两个 Python 3.13 应用程序:Python 3.13 和 Python 3.13t。后者是实验性的无 GIL Python。
python3.13t使用它就像在终端上输入一样简单。
现在,让我们打开两个终端窗口并sysconfig.get_config_var("Py_GIL_DISABLED")在两个不同的 Python 3.13 版本下运行:
欢迎来到无 GIL 世界!
为了感受它的威力,让我们编写一个简单的测试脚本来查看无 GIL Python 是否真的使多线程更快:
导入线程
导入时间
defcpu_bound_task ( n, thread_id ):
count = 0
for i inrange (n):
count += i*i
N = 100000000
defrun_with_threads ():
threads = []
start = time.time()
# 创建并启动 4 个线程
for i inrange ( 4 ):
t = threading.Thread(target=cpu_bound_task, args=(N, i))
threads.append(t)
t.start()
# 等待所有线程完成
for t inthreads :
t.join()
end = time.time()
print ( f'Total time taken: {end - start: .2 f} seconds' )
if __name__ == '__main__' :
run_with_threads()
上述程序构建了 4 个线程,每个线程执行相同的 CPU 密集型任务。我们先在标准 Python 3.13 中运行它:
13.51秒。这还不够快,不是吗?
现在是时候尝试 Python 3.13t(无 GIL Python)了:
3.74秒!
数字告诉了我们一切——没有 GIL,真正的多线程。
社区对 No-GIL Python 的回应
没有一种编程语言是一座孤岛。
这一大胆举措的成功取决于整个 Python 社区。
到目前为止,未来看起来一片光明。
一方面,许多软件工程师,尤其是来自数据科学和人工智能社区的软件工程师,热切期待性能的提升。
另一方面,包括Meta在内的主要技术公司已投入大量资源来支持这一转变。
这对 Python 开发人员意味着什么
有 GIL 还是没有 GIL,这是个问题。
在可预见的未来,这将不再是一个问题。
然而,考虑到无 GIL Python 预计将在 2028 年左右成为默认版本,而当前的无 GIL Python 3.13 仅仅是一个实验版本,我们现在不应该在生产中使用无 GIL Python。
因为仍然存在许多不确定性——包括兼容性、稳定性和生态系统的采用。它需要时间来完善。
但我们绝对应该关注无 GIL Python 的进展。它已经发生了,而且非常令人兴奋!
生命短暂,现在你又多了一个选择 Python 的理由。
| 本文为译文,英文原文地址(可能需要使用魔法访问或付费会员才可观看):medium.com/techtofreed… |
|---|