Python全局解释器锁定

如果你刚刚开始使用Python, 并且想了解更多信息, 请参加srcmini的Python数据科学入门课程。
全局解释器锁(GIL)是一种在解释器上应用全局锁的机制。它在计算机语言解释器中用于同步和管理线程的执行, 以便一次只能执行一个本地线程(由操作系统调度)。
在有多个线程的情况下, 可能发生的情况是两个线程都可能尝试同时获取内存, 结果它们将覆盖内存中的数据。因此, 需要一种可以帮助防止这种现象的机制。
一些流行的具有GIL的解释器是CPython和Ruby MRI。众所周知, Python是一种解释型语言, 它具有各种分布, 例如CPython, Jython和IronPython。其中, 仅CPython支持GIL, 它也是Python使用最广泛的实现。 CPython已使用C和Python语言进行开发, 主要是为了支持和使用内部包含许多C语言的应用程序。
即使你的处理器具有多个内核, 全局解释器一次也只允许执行一个线程。这是因为, 当线程开始运行时, 它会获取全局解释器锁。当它等待任何I / O操作(从磁盘读取数据/向磁盘写入数据)或CPU绑定操作(向量/矩阵乘法)时, 它将释放锁定, 以便该进程的其他线程可以运行。因此, 它阻止你同时运行其他线程。

Python全局解释器锁定

文章图片
让我们花点时间了解上图。如你所见, 有一个阶乘函数, 两个线程1和2, 线程1处于锁定状态, 而线程2处于等待状态。这意味着只有一个线程能够访问该功能。现在, 假设阶乘函数需要2秒钟才能完成。然后, 在理想情况下, 两个线程都应该能够在2秒内完成执行。但是, 在Python中不是这种情况, 两个线程都将串行运行, 而不会彼此并行。
调用阶乘函数的线程1和2所花费的时间可能是调用该函数两次的单个线程所花费的时间的两倍。这还告诉你, 由解释器控制的内存管理器不是线程安全的, 这意味着多个线程无法同时访问相同的共享数据。
因此, GIL(来源:了解Python GIL):
  • 限制线程操作。
  • 并行执行受到限制。
  • GIL确保一次只有一个线程在解释器中运行。
  • 它有助于简化各种底层细节, 例如内存管理。
使用GIL, 可以实现协作计算或协同多任务处理, 而不是并行计算。
Python全局解释器锁定

文章图片
【Python全局解释器锁定】资源
从上图可以看出, 最初有3个线程正在运行, 线程1已获取GIL, 并且当完成I / O操作(如读, 写等)时, 线程1释放了GIL。然后由线程2进行获取。此循环继续进行, 并且GIL交替更改线程, 直到线程完成程序的执行为止。请记住, 如果任何没有锁并且尚未完成执行的线程都将处于等待状态。
Python2和Python3中的GIL
Python全局解释器锁定

文章图片
与Python3相比, Python 2.7中的全局解释器锁定的工作方式有所不同。在纯CPU绑定操作中, 线程将继续运行, 因为没有I / O操作, 在这种情况下, 其他线程将处于空闲或等待状态, 这不是你想要的。为了解决这个问题, Python2具有称为” 滴答” 的术语。全局解释器锁执行检查以监视类似线程的等待状态, I / O操作或它是否正在运行的状态。但是, GIL不会一直在每个实例或时间监视线程, 而是使用滴答声的概念, 并且每隔100个滴答声检查一次线程。
滴答是一种字节代码指令, 当100个字节代码指令完成时, GIL会检查线程是否正在运行或处于等待状态。重要的是要记住, 滴答与时间无关, 因为它是字节码指令。与另一个相比, 每个刻度可能需要更长或更短的时间来运行。
Python全局解释器锁定

文章图片
资源
每100个滴答之后对线程进行一次定期检查是必不可少的, 尤其是在CPU绑定的操作中, 因为它们没有任何I / O操作。请注意, 你可以使用Python的内置sys模块修改滴答计数器。
Python全局解释器锁定

文章图片
资源
让我们讨论上述方法的一些缺点:
现在, 如果你查看此图, 你可能会注意到线程不是彼此并行运行而是以串行顺序运行, 也称为由全局解释器锁控制的协作计算。同样, 线程必须等待更长的持续时间。例如, 线程T3必须等待线程T1和T2都释放GIL。因此, 线程饿于锁定和计算。
另一个缺点是, 线程之间将争夺哪个线程将获得GIL。最初, 基于优先级T1可能会有GIL, 然后是T2, 依此类推。但是, 当T3释放GIL时, 它将向所有其他线程发送通知, 在这种情况下, 所有其他线程将继续争取获取GIL。话虽如此, 你可以通过设置每个线程的优先级来克服这一问题。
在Python3中, 你为每个线程分配了固定时间, 即5ms的执行时间。由于所有线程的等待时间相等, 因此可以防止线程因资源不足而饥饿。线程将等待查看线程1是否由于I / O或睡眠操作而故意释放了GIL。如果不是, 它将迫使它在5毫秒后释放。因此, 由于每个线程将获得相等的时间量, 因此不会浪费CPU时间, 这将消除获取GIL的挑战。
Python全局解释器锁定

文章图片
但是, 必须注意, 与I / O操作相比, GIL的新实现为CPU绑定的作业提供了更高的优先级。在一个线程释放了GIL并且两个线程正在等待的情况下, 与另一个线程相比, 该线程将需要GIL, 该线程需要运行CPU绑定的作业。
恭喜你完成了本教程。
本教程涵盖了一个高级主题, 目的是为你提供GIL在Python中的工作原理的理论概述。由于本教程没有涵盖GIL和线程的任何编码方面, 因此对大家来说, 一个不错的练习是通过创建多个线程和一种执行I / O密集型任务并最终分析GIL切换方式的方法自己进行试验。每个线程之间。
请随时在下面的评论部分中提出与本教程相关的任何问题。
参考文献:
  • 了解Python GIL
如果你刚刚开始使用Python, 并且想了解更多信息, 请参加srcmini的Python数据科学入门课程。

    推荐阅读