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 并发程序至关重要,特别是选择使用线程还是进程来解决特定的并发问题。