Python 有可能删除 GIL 吗?

栗子
栗子
发布时间:2026-10-14 | 5人浏览过
原创

本文深入解析 Python 全局解释器锁(GIL)的起源、历史移除尝试及当前现状。从单核时代的引用计数机制到多核时代的性能瓶颈,探讨了为何 GIL 难以移除。分析了 Greg Stein 等早期尝试失败的原因,以及 Python 3.2 对 GIL 的优化。结论指出,鉴于庞大的 C 扩展生态和单线程性能考量,Python 移除 GIL 的可能性极低,开发者应通过多进程或异步编程规避其限制。

在 CPython 解释器中,存在一个名为全局解释器锁(Global Interpreter Lock,简称 GIL)的互斥锁。它的主要作用是保护 Python 内部对象,确保同一时刻只有一个线程执行 Python 字节码,从而保障线程安全。然而,这也导致 Python 的线程无法充分利用多核 CPU 的计算能力,因此常被诟病为“伪多线程”,性能表现受限。那么,Python 未来是否有可能彻底移除 GIL 呢?

要解答这一疑问,我们需要从 GIL 的设计初衷和历史背景入手进行分析。

GIL 的起源

Python 首次发布于 1991 年,当时的计算机 CPU 均为单核架构。在单核环境下,多线程的设计初衷是为了实现 I/O 操作与 CPU 计算的重叠执行,以提高效率。Python 编译器由 C 语言编写,故称为 CPython。在那个年代,许多编程语言尚未具备自动内存管理机制。为了实现自动垃圾回收,Python 为每个对象引入了引用计数机制:当对象的引用计数降为 0 时,系统即可回收该对象并释放内存。例如:


>>> import sys
>>> data = { 'gzh': 'Python七号'}
>>> var1 = data
>>> sys.getrefcount(data)
3
>>> 

在上述代码中,data 对象拥有 3 个引用:对象本身、变量 var1 以及 getrefcount 函数的参数。若此时有另一个线程引用 data,其引用计数将增加 1;反之,若某线程使用完 data 后结束,引用计数则减 1。在多线程环境下,多个线程同时修改同一变量的“引用计数”会引发竞争条件(race conditions)。为避免此类问题,最简单有效的方案便是引入互斥锁进行保护。

若对每个对象都单独加锁,可能引发死锁问题,且频繁的加锁与解锁操作会显著降低性能。因此,最简单有效的策略是引入解释器锁(GIL):线程在执行任何字节码前必须先获取该锁。这既避免了死锁,又未造成过多性能损耗。在当时的单核 CPU 环境下,这种 GIL 设计简单且不影响性能,因此一直沿用至今。GIL 存在的最根本原因,在于 Python 的内存管理机制并非线程安全,这也是 GIL 产生并长期存在的核心缘由。

尝试消除 GIL

随着 CPU 进入多核时代,计算机能够同时执行多个计算任务,GIL 才真正成为了性能瓶颈。早在 1999 年,开发者 Greg Stein 基于 Python 1.5 版本尝试移除 GIL,取而代之的是在可变数据结构上应用更细粒度的锁,并提交了补丁以消除对全局可变对象的依赖。然而,在标准测试中,移除 GIL 后单线程性能反而比保留时慢了近 2 倍,测试环境还是当时性能最强的 Windows 机器。这意味着,即使使用 2 个 CPU 核心,其性能提升也仅略优于原来的 1 个核心,这种提升显然得不偿失,Greg Stein 的尝试最终宣告失败。

Python 之父 Guido van Rossum 曾欢迎社区志愿者尝试移除 GIL,前提是不降低单线程性能,但他也明确指出,移除 GIL 并非易事。

Python 开发者邮件列表中偶尔会出现关于移除 GIL 的讨论,但任何方案必须满足以下严苛条件:

  1. 简单性。从长远来看,该方案必须具备可实施性和可维护性。
  2. 并发提升。移除 GIL 必须能显著提升多线程性能。
  3. 速度保障。移除 GIL 绝不能降低单线程的执行性能。
  4. 兼容 CPython 特性。方案必须支持 CPython 的现有功能,例如 __del__ 析构函数和弱引用机制。
  5. API 兼容性。方案必须在源码层面与所有现有 CPython 扩展使用的宏保持兼容。
  6. 及时销毁不可达对象,以回收内存。
  7. 有序销毁。例如,若不可达对象 X 引用了 A,则必须在销毁 A 之前先销毁 X(部分垃圾回收算法无法保证这一点)。

部分需求难以满足,例如上述第 4、5、7 点。截至目前,尚未有人能在同时满足以上所有需求的前提下成功移除 GIL。

积重难返

近年来 Python 的火爆导致大量优秀库基于 CPython 开发,其中许多是 90 年代的 C 扩展库。若要移除 GIL,这些基于 GIL 机制编写的 C 扩展将无法继续使用。换言之,一旦移除 GIL,Python 生态系统中大量的扩展和第三方库将失效。

另一个明显的例子是,Python 解释器并非只有 CPython,还有基于 Java 编写的 Jython 和基于 .NET 实现的 IronPython,这些解释器完全没有 GIL。然而,为它们编写扩展的用户群体相对较少。

Python 之所以如此流行,很大程度上归功于其丰富的开箱即用的第三方库。由于历史包袱沉重(积重难返),移除 GIL 变得极其困难。

为什么 Python3 一开始时不去除 GIL

Python 3 在初期本有机会实现许多新功能,但这一过程打破了许多现有的 C 扩展,导致需要大量更新和移植工作以适配 Python 3,这也是 Python 3 初期未被社区广泛接受的原因之一。

与 Python 2 相比,若删除 GIL,将使 Python 3 在单线程性能上变得更慢,且大量优秀扩展将无法使用。可以想象,若真如此,Python 3 将没有未来,因此最终结果仍是 Python 3 保留了 GIL。

不过,Python 3 也对现有的 GIL 进行了重大改进。在 Python 3.2 版本中,确保了计算密集型线程和 I/O 密集型线程共存时,I/O 密集型线程长期无法获取 GIL 而无法执行的问题得到解决,从而提升了多线程的整体性能。

最后的话

由于 Python 的内存管理并非线程安全,自诞生之初便自带 GIL。随着时间推移,大量扩展在 GIL 的保护下编写,历史包袱日益沉重。Python 3 初期也因移除 GIL 导致单线程性能下降而选择保留 GIL。如今已是 Python 3.9 版本,未来 Python 移除 GIL 的可能性微乎其微。换句话说,一个没有 GIL 的 Python 可能已不再是我们要认识的那个 Python 了。

不过不必沮丧,GIL 影响的仅仅是多线程执行计算密集型任务的场景,而大多数程序员很少遇到此类场景。即使遇到,也可以通过多进程来规避 GIL 的影响,或者使用其他编程语言实现。任何编程语言或技术都不是十全十美的,发挥其长处才是最重要的。即使有 GIL,我依然会选择使用 Python。

以上就是关于“Python 有可能删除 GIL 吗?”的详细内容,更多关于 Python 删除 GIL 的资料请关注本站其它相关文章!

本站声明

本文内容仅供参考,不代表本站观点。转载或引用请注明出处。如发现内容问题,请通过联系我们页面反馈。

Python 相关产品和应用

全部下载

专题列表