什么是 Python 全局解释器锁 (GIL)
Python 全局解释器锁(GIL)是一种进程锁,GIL 确保单个进程中一次只有一个线程执行 Python 字节码。
- 在Python中,单线程进程和多线程进程的性能是相同的,这是因为Python中的GIL。
- 我们无法在Python中实现多线程,因为我们有全局解释器锁,它限制了线程并作为单线程工作。
- GIL 可能成为多线程 Python 程序中的瓶颈,特别是在需要并行执行 CPU 密集型任务的情况下。这是因为在任何给定时刻只有一个线程可以执行 Python 字节码。
- GIL 主要影响并行性(同时执行多个任务),但不影响并发性(同时执行多个任务的外观)。Python 线程对于并发 I/O 密集型任务或涉及等待外部资源的任务仍然有用。
1992 年Guido van Rossum 为了解决 CPython 解释器中的竞争条件,添加了一个众所周知的锁,称为全局解释器锁(Global Interpreter Lock),简称 GIL。二十五年后,这是我们都喜爱和使用的 Python 解释器的主要缺点之一。
GIL 为 Python 解决了什么问题:
Python 拥有其他语言所没有的东西,那就是引用计数器。借助引用计数器,我们可以计算 python 内部为数据对象赋值的引用总数。由于这个计数器,我们可以对引用进行计数,当计数达到零时,变量或数据对象将自动释放。例如
# Python program showing |
这个引用计数器变量需要受到保护,因为有时两个线程同时增加或减少其值,这样做可能会导致内存泄漏,因此为了保护线程,我们向跨线程共享的所有数据结构添加锁,但有时通过添加锁 存在多个锁会导致另一个问题,即死锁。为了避免内存泄漏和死锁问题,我们在解释器上使用单锁,即全局解释器锁(GIL)。
为什么选择GIL作为解决方案:
Python后端支持C语言,并且Python拥有的所有相关库大多是用C和C++编写的。由于 GIL,Python 提供了一种更好的方法来处理线程安全的内存管理。全局解释器锁在Python中很容易实现,因为它只需要为Python中的处理线程提供一个锁。GIL 实现起来很简单,并且很容易添加到 Python 中。它提高了单线程程序的性能,因为只需要管理一个锁。
对多线程 Python 程序的影响:
当用户编写 Python 程序或任何计算机程序时,受 CPU 限制的程序和受 I/O 限制的程序之间的性能存在差异。CPU 通过同时执行许多操作将程序推向极限,而 I/O 程序必须花费时间等待输入/输出。
代码 1:执行简单倒计时的 CPU 密集型程序
# Python program showing |
输出:所用时间(以秒为单位) - 2.5236213207244873
代码2:两个线程并行运行
# Python program showing |
输出:所用时间(以秒为单位) - 2.183610439300537
在上面的代码中,CPU 密集型进程和多线程进程的两段代码具有相同的性能,因为在 CPU 密集型程序中,因为 GIL 限制 CPU 只能使用单个线程。CPU 密集型线程和多线程的影响在 python 中是相同的。
为什么 GIL 还没有被删除:
GIL 到目前为止还没有得到改进,因为 python 2 具有 GIL 实现,如果我们在 python 3 中更改它,那么它将给我们带来问题。所以我们不是去掉GIL,而是改进GIL的概念。
目前还没有删除 GIL 的原因之一是 python 后端严重依赖 C,而 C 扩展严重依赖 GIL 的实现方法。
尽管有很多方法可以解决 GIL 解决的问题,但大多数方法都很难实现并且会减慢系统速度。
如何处理Python的GIL:
大多数时候我们使用多处理来防止程序出现GIL。在此实现中,python 为每个进程提供不同的解释器来运行,因此在这种情况下,为多处理中的每个进程提供单线程。
# Python program showing |
输出:
所用时间(以秒为单位) - 2.5148496627807617
可以看到,多线程系统和多处理系统所花费的时间没有区别。这是因为多处理系统有自己的问题需要解决。所以这并不能解决问题,但是它提供了 GIL 允许由 python 执行的解决方案。