python GIL

CPython 中的 GIL:影响与应对方法

Python 官方文档


global interpreter lock:
The mechanism used by the CPython interpreter to assure that only one thread executes Python bytecode at a time. This simplifies the CPython implementation by making the object model (including critical built-in types such as dict) implicitly safe against concurrent access. Locking the entire interpreter makes it easier for the interpreter to be multi-threaded, at the expense of much of the parallelism afforded by multi-processor machines.

However, some extension modules, either standard or third-party, are designed so as to release the GIL when doing computationally intensive tasks such as compression or hashing. Also, the GIL is always released when doing I/O.

Past efforts to create a “free-threaded” interpreter (one which locks shared data at a much finer granularity) have not been successful because performance suffered in the common single-processor case. It is believed that overcoming this performance issue would make the implementation much more complicated and therefore costlier to maintain.


全局解释程序锁:
全局解释程序锁(GIL)是 CPython 解释器的一个特性,确保一次只有一个线程可以执行 Python 字节码。GIL 的存在简化了 CPython 的实现,通过隐式地为对象模型提供并发访问安全性,甚至包括关键的内置类型,如字典。虽然全局解释程序锁会限制多线程环境中并行执行的能力,但通过将同步复杂性集中到解释器级别,它使得解释器更容易支持多线程。然而,这也导致多核处理器提供的并行性大部分被消耗。
不过,一些扩展模块(无论是标准的还是第三方的)被设计为在执行计算密集型任务(如压缩或散列)时会释放 GIL。此外,在执行 I/O 操作时,GIL 总是会被释放。
过去创建“自由线程”解释器(以更细的粒度锁定共享数据的解释器)的努力没有成功,因为在常见的单处理器情况下性能受到影响。据信,克服该性能问题将使实现复杂得多,并且因此维护成本更高。


不同 Python 解释器的特点及 GIL 影响

Python 生态系统中有多种 Python 解释器实现,每个实现都有其独特的特点:

  • CPython:作为 Python 的标准解释器,使用 C 语言开发,对 Python 语言特性的支持最全面,广泛被使用。
  • Jython:基于 Java 开发,能够将 Python 代码编译为 Java 字节码,运行在 JVM 上,且可以调用 Java 库。
  • IronPython:使用 C# 开发,运行在 .NET 平台上,可以调用 C# 库和 .NET 框架。
  • PyPy:使用 Python 开发,通过 JIT 技术提高执行效率,与 CPython 兼容性较好。
  • Cython:并非独立的解释器,而是一种能够将 Python 扩展为可编译成 C 语言的语法扩展,可以生成 C 语言代码以提高效率,需要编译后才能在 CPython 中使用。
  • 还有其他实现如 Stackless Python、MicroPython 等。
  • CPython 作为使用 C 语言开发的标准解释器,存在全局解释器锁(GIL)的问题,从而导致多线程无法充分利用多核 CPU。

  • Jython 和 IronPython:由于基于 Java 和 .NET 运行时,不存在 GIL 问题,可以在多核 CPU 上充分发挥优势。

  • PyPy:虽然 PyPy 也有 GIL,但其使用 JIT 编译技术,部分优化了 GIL 带来的问题。

  • Cython:Cython 本身并非独立的解释器,但它可以通过编译成 C 语言的扩展来提高 Python 解释器的效率,不受 GIL 影响。


为什么 CPython 中存在 GIL?

CPython 中存在全局解释器锁(GIL)的主要原因是其历史和设计初衷。

历史背景: Python 最初在单核 CPU 时代诞生,其设计初衷并未考虑多线程并发执行的场景。在此背景下,设计师并没有着重解决多线程竞争问题。

引用计数内存管理机制: CPython 主要使用引用计数内存管理机制。然而,这种机制并不是线程安全的。具体而言,引用计数机制在多线程环境中容易出现问题,多个线程同时对对象引用计数进行增减操作可能引发竞争条件和线程安全问题。

为了避免这些问题,CPython 在执行 Python 字节码时引入了 GIL。这个机制确保同一时刻只有一个线程可以执行 Python 字节码,从而简化了内存管理。然而,这也限制了在多核 CPU 下实现真正的并发执行。

尽管 GIL 在某些情况下降低了多线程编程的便利性,但它在某些方面也有好处,例如在 I/O 密集型任务中释放 GIL,允许其他线程执行。

CPython 使用 C 扩展库: CPython 广泛使用 C 语言扩展库来提升性能。在多线程环境下,C 扩展库需要自己实现线程同步,这可能增加复杂性和错误的可能性。

降低 GIL 影响的方法:

降低 GIL 影响的策略包括但不限于以下方法:

  1. 利用多进程充分利用多核 CPU,从而避免 GIL 的限制。
  2. 使用异步编程来优化 I/O 密集型应用的并发性。
  3. 使用 C 扩展库或 Cython 优化计算密集型代码的执行效率。
  4. 利用 multiprocessing 模块提供的进程池实现多进程并发。
  5. 考虑使用无 GIL 的解释器,如 Jython、IronPython。
  6. 使用并行计算框架如 Dask、Joblib 支持并行计算。
  7. 优化算法和数据结构以减少竞争锁的使用。
  8. 分离计算密集型代码和 I/O 操作,充分利用 GIL 的释放。
  9. 对于 I/O 密集型任务,使用多线程优化而非计算密集型任务。
  10. 合理设置线程数量,避免过多线程竞争。
  11. 使用原生线程或 asyncio 等作为并发手段。
  12. 在适当的情况下使用共享内存和消息传递,减少锁的竞争。

:D 一言句子获取中...