Skip to content

什么是 Python GIL (Global Interpreter Lock)?

GIL 全称是 Global Interpreter Lock,直译为 全局解释器锁。 它并不是 Python 语言本身的特性,而是 CPython (官方的,也是最常用的 Python 解释器实现) 的一个底层实现细节

本质上,GIL 是一个 互斥锁 (mutex),它保护了对 CPython 内部对象的访问,阻止多个原生线程 (native threads) 在同一时刻执行 Python 字节码

为什么 CPython 引入 GIL?

GIL 最初被引入 CPython 是为了简化 CPython 内部的内存管理(引用计数 (reference counting))。在 CPython 中,每个 Python 对象都有一个引用计数器,用于跟踪有多少个变量引用了该对象。当引用计数变为 0 时,该对象就可以被垃圾回收。如果没有 GIL,在多线程环境中,多个线程可能同时修改同一个对象的引用计数,导致竞争条件 (race condition),使得引用计数不准确,进而引发内存泄漏或程序崩溃。有了 GIL 后,任何时候只有一个线程能够执行 Python 字节码,因此对引用计数的操作是原子的,避免了复杂的细粒度锁定。

GIL 对 Python 程序的作用和影响是什么?

  1. 阻止多线程实现真正的并行计算 (对于 CPU 密集型任务): 这是 GIL 最为人熟知的影响。尽管你的计算机可能有多个 CPU 核心,你的 Python 程序创建了多个线程,但由于 GIL 的存在,同一时刻只有一个线程能够执行 Python 代码。对于 CPU 密集型 (CPU-bound) 任务(如大量的数学计算、数据处理),即使你使用多线程,它们也无法在多个 CPU 核心上真正并行运行。一个线程获得 GIL 并执行计算时,其他需要执行 Python 代码的线程必须等待 GIL 被释放。这就像你有很多事情要做(多个线程),但只有一个工具(GIL),所以你只能一个一个来。在某些情况下,使用多线程处理 CPU 密集型任务甚至可能比单线程更慢,因为会引入线程切换 (context switching) 的开销,而这些切换并没有带来并行执行的好处。
  2. 不阻止多线程实现并发 (对于 I/O 密集型任务): GIL 不会阻止多线程实现并发,尤其是在处理 I/O 密集型 (I/O-bound) 任务时(如网络请求、文件读写、数据库操作)。当一个 Python 线程执行到 I/O 操作时(比如 time.sleep() 或等待网络响应),它会主动释放 GIL。这时,其他等待 GIL 的线程就可以获得 GIL 并执行它们的 Python 代码。这样,当一个线程在等待 I/O 完成时,另一个线程可以利用 CPU 执行其他任务。这使得多个 I/O 密集型任务可以并发进行,大大提高了程序的效率。
  3. 对多进程 (Multiprocessing) 无影响: Python 的 multiprocessing 模块创建的是独立的进程,而不是线程。每个进程都有自己独立的 Python 解释器实例,因此每个进程都有自己的 GIL。这意味着,使用 multiprocessing 时,不同的进程可以在不同的 CPU 核心上并行执行 Python 代码,不受 GIL 的限制。这是在 Python 中实现真正的 CPU 并行计算的标准方法。
  4. 简化 CPython 内部实现: 虽然 GIL 限制了多线程的并行性,但它确实大大简化了 CPython 解释器的内部实现,使得引用计数等操作更容易管理。这被认为是 CPython 开发早期为了快速发展而做出的权衡。
  5. 并非所有 Python 实现都有 GIL: 需要再次强调,GIL 是 CPython 的特性。像 Jython (基于 Java 虚拟机)、IronPython (基于 .NET 平台) 等其他 Python 实现,它们通常会利用底层平台(JVM 或 CLR)的成熟多线程模型,因此没有 GIL。PyPy (使用 JIT 技术) 有自己的 GIL 实现,但其内部设计与 CPython 不同。

总结:

  • Python GIL 是 CPython 解释器的一个锁,它保证同一时刻只有一个原生线程能够执行 Python 字节码。它主要是为了简化 CPython 的内存管理 (引用计数)。GIL 是 CPython 特有,并非所有 Python 实现都有。
  • 影响/不影响: 阻止 Python 多线程实现真正的 CPU 并行计算,对 CPU 密集型任务影响最大;允许 Python 多线程I/O 密集型任务上的并发能力,因为线程在等待 I/O 时会释放 GIL。
  • 规避方法: 使用 多进程 (multiprocessing) 来实现真正的 CPU 并行计算,因为每个进程都有独立的 GIL。

理解 GIL 对于正确地设计和优化 Python 并发程序至关重要,特别是选择使用线程还是进程来解决特定的并发问题。