课程
如果您刚开始学习 Python 并希望了解更多内容,推荐参加 DataCamp 的 Python 数据科学入门 课程。
全局解释器锁(GIL)是一种在解释器上施加全局锁的机制。它用于计算机语言解释器中,同步并管理线程的执行,使得在任一时刻只有一个由操作系统调度的原生线程可以执行。
在存在多个线程的场景中,可能出现两条线程同时尝试获取内存的情况,进而导致彼此覆盖内存中的数据。因此,需要一种机制来避免这一现象。
一些带有 GIL 的常见解释器包括 CPython 和 Ruby MRI。众所周知,Python 是一门解释型语言,拥有多个发行版,如 CPython、Jython、IronPython。其中只有 CPython 支持 GIL,它也是使用最广泛的 Python 实现。CPython 主要用 C 语言和 Python 开发,旨在良好支持并与底层大量使用 C 语言的应用协同工作。
即使您的处理器有多个核心,全局解释器锁也只允许一次执行一个线程。这是因为,当某个线程开始运行时,会获取全局解释器锁。当它等待任何 I/O 操作(从磁盘读/写数据)或 CPU 密集型操作(向量/矩阵乘法)时,会释放该锁,以便该进程的其他线程运行。因此,GIL 会阻止您同时运行其他线程。

我们来理解一下上图。可以看到有一个 factorial 函数和两个线程 1 与 2,线程 1 处于锁定状态,而线程 2 处于等待状态。这意味着同一时刻只有一条线程能够访问该函数。现在,假设 factorial 函数需要 2 秒完成。那么在理想情况下,两条线程都应能在 2 秒内完成。但在 Python 中并非如此,两条线程会串行运行,而非并行。
线程 1 和 2 调用 factorial 函数所花的时间,可能会是单个线程连续调用两次的两倍。这也说明由解释器管理的内存管理器不是 线程安全 的,即多个线程无法同时访问相同的共享数据。
因此,GIL(来源:Understanding the Python GIL):
-
限制线程化操作。
-
并行执行受限。
-
GIL 确保同一时刻仅有一个线程在解释器中运行。
-
有助于简化内存管理等多种底层细节。
在 GIL 下,达成的是协同(合作式)计算或协调式多任务,而非真正的并行计算。
从上图可以看出,开始时有三条线程,线程 1 正在运行并已获取 GIL,当执行读、写等 I/O 操作时,线程 1 释放 GIL,随后线程 2 获取 GIL。这个循环不断进行,GIL 在各线程之间交替切换,直到所有线程完成程序执行。请记住,任何未持有锁且未完成执行的线程都将处于等待状态。
Python2 与 Python3 中的 GIL

Python 2.7 的全局解释器锁与 Python 3 的工作方式不同。在纯 CPU 密集型操作中,由于没有 I/O 操作,线程会持续运行,其他线程则处于空闲或等待状态,这并非理想行为。为解决该问题,Python2 引入了 ticks 的概念。全局解释器锁会检查线程状态,如等待、I/O 操作或正在运行。不过,GIL 并不会持续不断地监控线程,而是基于 ticks 的概念,每 100 个 tick 检查一次线程。
一个 tick 即一个字节码指令,当完成 100 条字节码指令后,GIL 会检查线程是否在运行或处于等待状态。需要注意,tick 与时间无关,因为它是字节码指令。每个 tick 的运行时长可能不尽相同。
这种每 100 个 tick 定期检查线程的方式在 CPU 密集型操作中尤为关键,因为这类操作没有 I/O。注意,您可以使用 Python 的内置 sys 模块修改 tick 计数器。
我们来讨论上述做法的一些缺点:
现在,如果您查看这张图,会注意到线程不是并行运行,而是串行执行,也就是由全局解释器锁控制的合作式计算。此外,线程需要等待较长时间。例如,线程 T3 必须等待 T1 和 T2 都释放 GIL。因此,线程在锁和计算资源上会出现“饥饿”。
另一个缺点是线程之间会争夺谁能够获取 GIL。最初可能基于优先级由 T1 获取 GIL,然后是 T2,依此类推。然而,当 T3 释放 GIL 时,它会向所有其他线程发送通知,其他线程便会争抢获取 GIL。不过,您可以通过设置各线程的优先级在一定程度上缓解这一问题。
在 Python3 中,每个线程被分配固定的执行时间(约 5ms)。这可避免线程因资源不足而“饥饿”,因为所有线程的等待时间相同。线程会先等待观察线程 1 是否因 I/O 或 sleep 操作主动释放 GIL;若未释放,将在约 5ms 后被强制释放。由此,CPU 时间不会出现饥饿现象,每个线程都能获得相等的时间,从而缓解对 GIL 的争夺。
不过需要注意的是,新的 GIL 实现相比 I/O 操作,会对 CPU 密集型任务给予更高优先级。当某个线程释放 GIL、而有两个线程在等待时,具有 CPU 密集型需求的线程将优先获得 GIL。
结语
恭喜您完成本教程。
本教程涵盖了一个进阶主题,旨在为您提供 Python 中 GIL 工作机制的理论概览。由于本文未涉及 GIL 与线程的具体编码实践,建议您自行尝试:创建多个线程与一个执行 I/O 密集型任务的方法,观察并分析 GIL 如何在线程之间切换。
如对本教程有任何疑问,欢迎在下方评论区留言。
参考资料:
如果您刚开始学习 Python 并希望了解更多内容,推荐参加 DataCamp 的 Python 数据科学入门 课程。