GIL 全局解释器锁
GIL -- global interpreter lock -- 全局解释器锁
1. GIL 是什么
CPython
是用 C 语言实现的 Python 解释器。作为官方实现,它是最广泛使用的 Python 解释器。除了 CPython 以外,还有用 Java 实现的 Jython
,用 .NET 实现的 IronPython
,使 Python 方便地和 Java 程序、.NET 程序集成。另外还有一些实验性的 Python 解释器比如 PyPy
。
GIL 是 CPython 解释器所采用的一种机制,它确保同一时刻只有一个线程在执行 Python bytecode
(字节码,其解释看下文)。此机制通过设置对象模型(包括 dict 等重要内置类型)针对并发访问的隐式安全简化了 CPython 实现。给整个解释器加锁使得解释器多线程运行更方便,其代价则是牺牲了在多处理器上的并行性。
bytecode -- 字节码
Python 源代码会被编译为字节码,即 CPython 解释器中表示 Python 程序的内部代码。字节码还会缓存在.pyc
文件中,这样第二次执行同一文件时速度更快(可以免去将源码重新编译为字节码)。这种 "中间语言" 运行在根据字节码执行相应机器码的 virtual machine 之上。请注意不同 Python 虚拟机上的字节码不一定通用,也不一定能在不同 Python 版本上兼容。
2. GIL 为什么存在
GIL 的存在更多的是历史原因。早期 CPython 都是基于在单核 CPU 的前提下设计的,没考虑过多核的情况。
2.1 GIL 的好处
- 提高单线程程序的速度。
- 轻松集成通常不是线程安全的 C 库。(C 扩展更容易)
- 对于 IO 密集型程序,它在多线程情况下更快。
2.2 为什么使用 GIL
- 在 CPython 中,全局解释器锁或 GIL 是一个互斥锁,可防止多个本机线程同时执行 Python 字节码。这个锁是必要的,主要是因为 CPython 的内存管理不是线程安全的。
- GIL 是有争议的,因为它阻止了多线程 CPython 程序在某些情况下充分利用多处理器系统。请注意,潜在的阻塞或长时间运行的操作(例如 I/O、图像处理和 NumPy 数字运算)发生在 GIL 之外。因此,只有在 GIL 中花费大量时间来解释 CPython 字节码的多线程程序中,GIL 才会成为瓶颈。
2.3 GIL 的影响
- 当且仅当您在纯 Python 中执行 CPU 密集型工作时,GIL 才是一个问题。
- 无法发挥现在 CPU 的多核优势。
3. 做了哪些优化
一些扩展模块,无论是标准的还是第三方的,都被设计成在执行压缩或散列等计算密集型任务时释放 GIL。 此外,在执行 I/O 时总是释放 GIL。
创建一个(以更精细粒度来锁定共享数据的)“自由线程”解释器的努力从未获得成功,因为这会牺牲在普通单处理器情况下的性能。据信克服这种性能问题的措施将导致实现变得更复杂,从而更难以维护。
4. 总结
- IO 密集型任务场景,可以使用多线程可以提高运行效率
- CPU 密集型任务场景,不要使用多线程,而是使用多进程