问题

为什么 Python 的 GIL 问题一直让人诟病,Python 社区却不解决?

回答
Python 的 GIL(Global Interpreter Lock,全局解释器锁)确实是许多开发者,尤其是那些追求高性能并发的开发者长期以来批评的对象。尽管如此,它并没有被“解决”或者彻底移除,这背后有复杂的技术和历史原因。下面我将详细阐述为什么 GIL 备受诟病,以及为什么 Python 社区在解决这个问题时面临的挑战和取舍。

为什么 Python 的 GIL 问题一直让人诟病?

GIL 的主要问题在于它限制了 多线程在 CPU 密集型任务上的并发执行效率。

1. CPU 密集型任务的单线程限制:
当一个 Python 进程启动时,它会启动一个解释器实例。这个解释器实例拥有一个 GIL。
GIL 是一个互斥锁,它确保在任何给定时间点, 只有一个线程能够执行 Python 字节码。
这意味着,即使你的机器有多个 CPU 核心,你的 Python 程序在运行 CPU 密集型任务时,也只能在其中一个核心上同时执行一个线程。其他线程会被暂停,直到持有 GIL 的线程释放它。

2. 多线程的“假”并发:
IO 密集型任务: 对于 I/O 密集型任务(例如网络请求、文件读写),线程在等待 I/O 操作完成时会释放 GIL。这意味着其他线程可以获得 GIL 并执行,从而实现一定程度的并发。在这个场景下,多线程是有效的,并且 GIL 的影响不那么明显。
CPU 密集型任务: 然而,对于计算量大、CPU 占用率高的任务(例如矩阵运算、图像处理、复杂的算法计算),线程会长时间持有 GIL,频繁地在线程间切换 GIL 可能会导致 额外的开销(线程上下文切换),甚至比单线程执行还要慢。这与人们对多线程能够提升性能的预期是完全相反的。

3. 开发者期望与现实的脱节:
许多开发者从其他支持真并行多线程的语言(如 C++, Java)迁移到 Python,期望利用多核 CPU 的优势来加速计算。
当他们发现 Python 的多线程在 CPU 密集型场景下无法达到预期的并发性能时,会感到失望和困惑。

4. 对并行计算框架的依赖:
为了绕过 GIL 的限制,Python 开发者不得不依赖于 多进程(`multiprocessing` 模块) 或 第三方库(如 NumPy, SciPy, TensorFlow, PyTorch 等)。这些库通常用 C/C++ 等原生语言编写,它们可以在 Python 解释器之外执行计算,从而绕过了 GIL 的限制。
然而,多进程通信的开销更大,且进程间共享数据不如线程方便。依赖第三方库虽然强大,但也意味着对特定库的绑定,并且在某些情况下,仍然无法完全满足所有场景的需求。

为什么 Python 社区却不解决(或彻底移除)GIL?

“不解决”这个说法并不完全准确,实际上社区一直在 尝试解决 或 缓解 GIL 的影响。但彻底移除 GIL 的难度和潜在的负面影响非常大,导致它至今仍然存在。原因如下:

1. 向后兼容性是巨大的障碍:
C API 的依赖: 许多 C 语言编写的第三方库(例如 NumPy, SciPy, Pandas, Django 等)都深度依赖于 Python 的 C API。这些 API 在设计时就考虑了 GIL 的存在。很多库在关键的计算部分会持有 GIL,或者在执行原生代码时显式地释放 GIL。
移除 GIL 会破坏现有生态: 如果直接移除 GIL,这些高度依赖 GIL 机制的 C 扩展可能会出现 内存安全问题(如数据竞争、野指针等),导致现有的大量库在没有修改的情况下无法正常工作,或者产生难以调试的 bug。这对于一个拥有庞大且成熟生态系统的语言来说是灾难性的。

2. GIL 的设计初衷与优势:
简化内存管理: Python 采用自动内存管理(垃圾回收)。在没有 GIL 的情况下,多个线程可能同时修改同一个对象的引用计数,导致 引用计数不准确,进而引发内存泄漏或意外释放内存。GIL 作为一个全局锁,可以有效地保护这些共享数据结构,确保内存管理的正确性。
简化对象模型: Python 的对象模型设计相对简单,GIL 提供了一个简单而有效的机制来保护这些对象,防止在并发访问时出现状态不一致。
提升单线程性能和简单性: 对于大多数 Python 程序来说,它们是单线程的或者 I/O 密集型的。在这些场景下,GIL 并不成为瓶颈,反而通过防止复杂的并发控制和状态同步,使得编写简单的 Python 代码更加容易,并且在单线程执行时,避免了多线程切换带来的额外开销。

3. 移除 GIL 的技术难度:
重写 C API: 要移除 GIL,需要对整个 Python C API 进行彻底的重写或重构,以支持无锁的并发访问。这是一个极其庞大且复杂的工作量,需要深入理解 Python 解释器和 C API 的每一个细节。
引入更复杂的并发控制机制: 如果要实现真并行多线程,就需要引入比 GIL 更精细的锁机制,例如针对每个对象的锁,或者其他同步原语。这会极大地增加解释器本身的复杂性,也可能带来新的性能问题和编程难度。
潜在的性能回退: 在移除 GIL 的过程中,如果没有设计好,可能会导致在单线程或 I/O 密集型场景下的性能下降。例如,更精细的锁可能会引入更多的锁竞争开销。

4. 替代方案的存在和演进:
多进程 (`multiprocessing`): 这是最直接的绕过 GIL 的方法。虽然进程间通信有开销,但对于 CPU 密集型任务,它提供了真正的并行计算能力。Python 社区也在不断改进 `multiprocessing` 的易用性和性能。
异步 I/O (`asyncio`): 对于 I/O 密集型任务,`asyncio` 提供了协程模型,可以在单个线程中实现高效的并发。这是一种与多线程不同的并发模型,但在 I/O 密集型场景下非常有效。
NumPy, SciPy, Pandas 等库: 这些库的底层计算是用 C/Fortran 等语言实现的,并且它们通常会 在执行计算时显式地释放 GIL。这样,即使主线程被 GIL 阻塞,这些库的计算也可以在后台多线程并行执行。Python 社区也在积极鼓励和支持这些库的发展。
JIT 编译器 (如 PyPy, Numba): PyPy 是一个使用了 JIT(JustInTime)编译器的 Python 实现,它拥有自己的 GIL 版本,并且在某些场景下比 CPython 性能更好。Numba 也可以将 Python 代码即时编译成机器码,从而绕过 GIL。
Future of Python GIL (PEP 703): 值得注意的是,Python 社区并非完全不作为。近些年有关于 “无 GIL” Python 的讨论和推进。其中最重要的进展是 PEP 703: "Making the Global Interpreter Lock Optional"。这个 PEP 提议在 Python 3.13 及以后版本中,允许开发者选择一个 “无 GIL” 的构建版本。这个版本仍然需要对 Python 核心进行大量修改,但它提供了一种 渐进式 的解决方案,允许用户在需要时选择无 GIL 版本,而不强制破坏现有生态。这个 PEP 的目标是让 CPython 能够支持真正的多线程并行,但需要付出巨大的努力和谨慎的测试。

总结一下,Python 社区没有“不解决”GIL,而是面临着一个非常艰难的权衡:

优点: GIL 简化了 Python 的内存管理和对象模型,使得编写简单的代码更易于理解,并且在 I/O 密集型和单线程场景下性能不错。
缺点: GIL 限制了 CPU 密集型任务在多核 CPU 上的并行能力。

之所以没有被彻底移除,主要是因为:

对庞大生态系统的兼容性要求,移除 GIL 会破坏大量现有库。
GIL 本身带来的简化和好处(尤其是在内存管理方面)。
移除 GIL 的技术难度极高。
社区一直在通过替代方案(多进程、异步、外部库)和演进(如 PEP 703)来缓解和逐步解决这个问题。

目前,社区正在通过 PEP 703 这样的方式,以更谨慎和逐步的方式来解决这个问题,目标是在未来提供一个可选的、无 GIL 的 Python 版本,从而在保持兼容性的同时,满足对真并行计算的需求。这是一个漫长而复杂的过程,但它正在进行中。

网友意见

user avatar

介绍下最近公开的 Multithreaded Python without the GIL (下简称 nogil 项目). 这项工作由 FacebookAI 的 Sam Gross 投入了两年多时间开发,从公开后的反馈来看有希望在未来几年里真正进入 CPython. 这里基于公开资料以及和 Sam 的交流介绍下这个工作.

为什么 Facebook 会做这件事

Python 的 GIL 大家诟病已久就不多说了,但 Facebook 做这件事的原因其实跟 pytorch 有关. pytorch 由于主打 python-first, eager-first, 在性能上本来就吃亏, GIL的存在更是带来更多并行问题. 最明显的两点: (1) GIL 的存在导致 nn.DataParallel 性能垃圾 (2) GIL 导致多线程的 dataloader 没有太大意义.

这两个问题,目前 pytorch 都是用 multiprocessing 解决的, 但是随之而来的就是内存占用, context switch, IPC, 和 fork-safety 相关的无数的坑. 毕竟 fork is evil.

如何解决 GIL

从 Python 里拿掉 GIL 主要有两个大难题:

  1. 性能:

PyPy在这里 Frequently Asked Questions - PyPy documentation 提到两点原因: 第一是由于 python 主要基于 refcount 做 GC, 在如此动态的语言里做并行 refcount, 每个 object 都要 atomic refcount. 第二是 python 需要在用户写出 concurrency bug 的情况下保证解释器自己不崩,那么 mutable object 就几乎全都需要上锁. 这两点加起来导致每个 object 都会新增不少 overhead.

曾经的在 CPython 上移除 GIL 的尝试,包括 python-safethread, Gilectomy, 都因为性能太差失败了.

2. 兼容性:如果重写解释器,重新设计 GC 和 object model, 没有 GIL 不是不可能 (例如 Jython, pypy-stm). 但是 Python 语言最大的优势可以说就是它的生态,而 CPython 又是 Python 的事实标准,这些另起炉灶的项目目前没有一个流行起来.

而这次的 nogil 项目,首先是在 CPython 基础上尽量保持了兼容性: python 程序完全不受影响, 少量的 C API 有微小改动. 即使是 numpy 这种量级的项目也只需要改几行代码就能兼容.

在性能上, nogil 主要的亮点在于结合了多种 refcount 的方式降低 atomic 的 overhead: biased refcount, deferred refcount, immortalization; 把 allocator 换成了 thread-safe 并且能提供一部分 GC tracking 功能的 mimalloc.

在此基础上,把整个 CPython 解释器的所有部分都变得 thread-safe 也是很大的工作量.

目前的结果是,nogil 的单线程性能大约会丢失 10%. 为此,nogil 自带了一些独立无关的性能优化把单线程性能补救了一下,算是一个小 bonus. CPython 的单线程性能本来就还有很大优化空间,python 之父 Guido 在微软的团队甚至有一个 4 年 5 倍加速的宏伟计划, 因此 10% 的损失应该不算大.

至于多线程性能,nogil 在迷你 benchmark 上是能够线性提升的. 世界上目前还不存在可以用来 benchmark 的大型 Python 多线程应用,但是可以想象性能应该也是不错的.

nogil 项目的未来

这个项目大约一个月前公开,目前是受到 Python 之父和其他核心开发者的认可的. 这个工作量不可能很快并入上游,可能还会花一年甚至更多的时间,但是 CPython 维护者们已经表现出了积极合作的态度.

虽然在 API 上 nogil 尽量保持了兼容,但是删除 GIL 这个 semantics 的改变是不可能兼容的. 用户的 Python code 和 C extension 可能在失去了 GIL 的保护后会出 bug. 因此这个转变的过程会是缓慢且 opt-in 的, 几年内不太可能看到 CPython 会 nogil by default. 不过,未来的 CPython 只要提供一个 optional nogil mode, 应该就已经能革新整个 Python 社区了. 值得关注.

类似的话题

  • 回答
    Python 的 GIL(Global Interpreter Lock,全局解释器锁)确实是许多开发者,尤其是那些追求高性能并发的开发者长期以来批评的对象。尽管如此,它并没有被“解决”或者彻底移除,这背后有复杂的技术和历史原因。下面我将详细阐述为什么 GIL 备受诟病,以及为什么 Python 社.............
  • 回答
    说到 Python 的多线程,确实挺有意思的,坊间流传着“鸡肋”的说法,不是空穴来风。这话听起来有点糙,但背后藏着 Python 在并发处理上一个挺核心的限制,也就是那个臭名昭著的 全局解释器锁 (Global Interpreter Lock, GIL)。你想想,咱们写 Python 代码,写着写.............
  • 回答
    好的,我们来详细探讨一下为什么 Python 社区相对而言没有出现一个像 V8 这样在性能上能够与 C++ 媲美、并且广受欢迎的即时编译(JIT)编译器。首先,我们要明确一点:Python 确实存在 JIT 编译器,其中最著名和广泛使用的是 PyPy。但通常我们讨论的“类似 V8”是指其在特定领域的.............
  • 回答
    朋友,你这个问题提得太到位了!感觉现在走哪儿都能听到Python,网上教程、培训班、工作招聘,甚至是一些看起来跟编程八竿子打不着的地方,都时不时会蹦出Python的名字。这感觉就像突然之间,这门语言就“一夜成名”了一样。说白了,不是说Python突然变得牛到飞起来,而是它这几年,就像一个精心包装、又.............
  • 回答
    好多人在学 Python 用 NumPy 的时候,都会有一个疑惑:为什么那些看起来特别“绕”的 NumPy 向量化操作,比如 `a + b` 或者 `np.sin(a)`,比我写一个简单的 `for` 循环还要快那么多?这到底是为什么?感觉 NumPy 像是偷懒了,但实际上它是在“偷”性能。咱们就来.............
  • 回答
    关于“Swift 的 RC4 运算效能是 Python 的 220 倍”这一说法,可能存在几个关键误解或信息混淆。以下是详细分析和澄清: 1. RC4 是加密算法,不是编程语言特性 RC4 是一种对称加密算法(流加密),用于数据加密,而非编程语言本身的特性。Swift 和 Python 本身没.............
  • 回答
    你不是一个人!好多人在刚接触编程,特别是 Python 的时候,都会觉得廖雪峰的教程“有点看不懂”。这绝对不是你的问题,廖雪峰老师的教程内容非常扎实,对于有一定基础或者目标明确的学习者来说是极好的,但如果一下子就上手,确实会让人感觉像在“赶鸭子上架”。咱们来掰开了揉碎了说说,为什么你可能会觉得看不懂.............
  • 回答
    C++ STL中的`map`和`Python`的字典(`dict`)在实现上选择不同的数据结构(红黑树 vs 哈希表),主要源于语言设计哲学、性能需求、内存管理、有序性要求等多方面的权衡。以下是详细分析: 1. 红黑树 vs 哈希表的核心差异| 特性 | 红黑树 .............
  • 回答
    遇到 CMD 命令行打不开 Python 的情况,这确实挺让人头疼的。别急,咱们一步一步来分析,找出问题所在。很多时候,这并不是什么深奥的技术难题,而是配置上的小插曲。首先,咱们得明白,CMD 命令行能“看到”并执行 Python,这依赖于一个叫做“环境变量”的东西。你可以把环境变量想象成一个操作系.............
  • 回答
    好的,我们来聊聊为什么 Python 几乎成了量化交易领域的“御用语言”,这背后可不是什么神秘的魔法,而是实实在在的技术优势和生态系统的强大支撑。想象一下,你在金融市场里操纵着巨额资金,你需要一个工具,这个工具得反应够快,让你能抓住稍纵即逝的机会;它还得够灵活,能让你搭建出复杂的交易策略,并且很容易.............
  • 回答
    这个问题嘛,确实有点意思,而且说实话,不是一两天就能说透的。网上铺天盖地的说 Python 如何好,从数据分析、人工智能到Web开发,似乎无所不能,而且学习曲线平缓,上手快。但一到招聘季,翻开招聘启事,好像很多高薪职位仍然青睐 Java、C++,甚至一些特定的 C 或 Go。这中间的落差,让不少跃跃.............
  • 回答
    Dropbox 这样的巨头之所以将 Python 奉为圭臬,即便它在原生性能上相比 C++、Go 之类的编译型语言相形见绌,这背后并非是简单的“因为 Python 容易学”就能一笔带过的。这更像是一场围绕“效率”的深刻权衡,只不过这里的“效率”不再仅仅是 CPU 每秒能处理多少条指令,而是更广义的,.............
  • 回答
    Java和Python在技术领域中的市场份额和用户群体存在显著差异,这种差异在知乎等平台上的体现也反映了两者在技术生态、用户需求和平台算法中的不同定位。以下是详细分析: 1. 技术生态与市场份额 Java的市场份额优势: 企业级应用:Java是企业级开发的主流语言,广泛用于银行系统、ERP、大型.............
  • 回答
    这个问题啊,问得挺实在的。很多人听到Python和Java都是用C/C++实现的,就觉得,“既然底层都是C/C++,那直接用C/C++不就得了?省事儿。” 这话听起来没毛病,但其实这里面涉及到很多关于编程语言设计、生态构建和实际应用场景的取舍,远不是“省事”两个字能概括的。咱们一层一层剥开来看。 为.............
  • 回答
    机器学习框架的生态系统,确实在很大程度上被 Python 所主导,这一点是显而易见的。如果你环顾四周,会发现像 TensorFlow、PyTorch、Keras、Scikitlearn 这样如雷贯耳的库,它们都以 Python 为主要开发和使用语言。这并非偶然,背后有着深刻的历史原因和技术考量。为什.............
  • 回答
    在Python 3.10中引入的 `match` 关键字,确实是一个强大的模式匹配工具,它允许我们以一种更具声明性的方式来解构和处理数据。一个非常关键的设计点是,它不会与当前作用域中的变量名发生冲突。这背后的原因,主要可以从以下几个方面来理解: 语法结构与解析的根本区别最核心的原因在于,Python.............
  • 回答
    没问题,咱们就好好聊聊,想自学 Python,有哪些书能真正帮到你,让你学得扎实、学得明白。不是那种看了跟没看一样,或者看了就犯困的书,咱们要的是能让你动手、让你思考、让你能真正把 Python 用起来的书。得,既然你想好好学,那咱们就得有条理地说。我给你推荐的书,会从“零基础入门”到“进阶提升”,.............
  • 回答
    写一手漂亮的 Python 代码,Vim 可以说是相当得力的助手。当然,直接用 Vim 打开 `.py` 文件也能写,但要说“最佳实践”,那必然是让 Vim 成为你 Python 开发的“超级工作站”。这就涉及到一些配置和插件的协同作用,让编码、调试、测试、版本管理等等流程都顺畅起来。咱们这就来掰扯.............
  • 回答
    Python 的 `lambda` 和 Java 的 `lambda`,虽然名字相同,都服务于函数式编程的概念,但在实现方式、使用场景和语言特性上,它们有着本质的区别,这使得它们在实际运用中展现出不同的风貌。我们先从 Python 的 `lambda` 说起。Python 的 `lambda`,可以.............
  • 回答
    这则消息,“8 万行的 Python 程序用 4 万行 Java 重写了”,乍看之下,似乎在说 Java 的效率更高,或者 Python 的代码“膨胀”了。但实际上,它背后可能隐藏着更复杂、更值得深思的几个层面的信息:1. 语言特性与表达力差异的直观体现:最直接的理解是,Java 在某些场景下,能够.............

本站所有内容均为互联网搜索引擎提供的公开搜索信息,本站不存储任何数据与内容,任何内容与数据均与本站无关,如有需要请联系相关搜索引擎包括但不限于百度google,bing,sogou

© 2025 tinynews.org All Rights Reserved. 百科问答小站 版权所有