Python 的 GIL 是什么?全局解释器锁

2024 年 8 月 29 日 | 4 分钟阅读

本教程将重点介绍 Python 的一个重要主题,GIL。我们还将通过代码实现来介绍 GIL 如何影响 Python 程序的性能。在深入探讨这个主题之前,让我们先对 GIL 有一个基本的了解。

GIL 或全局解释器锁

Python 的全局解释器锁或 GIL 是多线程编程中的一个重要部分。它是一种在处理多个进程时使用的进程锁。它只允许一个线程获得控制权。通常,Python 使用单个线程来运行单个进程。使用 GIL,我们可以获得单线程和多线程进程相同的性能结果。它限制了 Python 中实现多线程,因为它阻止了线程,并且像单个线程一样工作。

注意 - Python 不支持多线程,因为 threading 包无法让我们使用多个 CPU 核心。

为什么 Python 开发者使用 GIL?

Python 提供了独特的引用计数器功能,用于内存管理。引用计数器计算 Python 内部为数据对象赋值的引用总数。当引用计数达到零时,对象的分配内存将被释放。让我们看下面的例子。

示例 -

引用计数变量的主要问题在于,当两个或三个线程同时尝试增加或减少其值时,它会受到影响。这被称为竞态条件。如果发生这种情况,可能会导致内存泄漏,永远无法释放。这可能会导致 Python 程序崩溃或出现错误。

GIL 通过使用锁来保护所有共享数据结构,使其不会在线程之间被不一致地修改,从而有助于消除这种情况。Python 提供了一种实现 GIL 的简单方法,因为它处理线程安全的内存管理。GIL 要求为 Python 中的处理提供单个锁给一个线程。它通过只需要处理一个锁来提高单线程程序的性能。它还有助于制作任何 CPU 密集型程序并防止死锁。

对多线程 Python 程序的影响

对于典型的 Python 程序或任何计算机程序,CPU 密集型程序的性能和 I/O 密集型程序的性能之间存在差异。CPU 密集型程序通常将 CPU 推到极限。这些程序通常用于数学计算,如矩阵乘法、搜索、图像处理等。

I/O 密集型程序是指花费时间获取输入/输出的程序,这些输入/输出可以由用户、文件、数据库、网络等生成。这些程序必须等待相当长的时间,直到源提供输入。另一方面,源也有其自身的处理时间。例如 - 用户正在考虑输入什么。

让我们理解下面的例子。

示例 -

输出

Time taken in seconds - 7.422671556472778

现在我们通过运行两个线程来修改上面的代码。

示例 - 2

输出

Time taken in seconds - 6.90830135345459

我们可以看到两种代码都花了相同的时间完成。GIL 阻止了第二个代码中的 CPU 密集型线程并行执行。

为什么 GIL 还没有被移除?

许多程序员对此表示不满,但 Python 无法进行像移除 GIL 这样重大的更改。另一个原因是 GIL 目前还没有得到改进。如果它在 Python 3 中发生变化,将会造成一些严重的问题。与其移除 GIL,不如改进 GIL 的概念。根据 Guido van Rossom 的说法:

"我欢迎一系列补丁进入 Py3k,前提是单线程程序(以及多线程但 I/O 密集型程序)的性能不会下降"。

还有许多方法可以解决 GIL 所解决的相同问题,但它们很难实现。

如何处理 Python 的 GIL

使用多进程是防止程序受到 GIL 影响的最合适方法。Python 为每个进程提供了不同的解释器,在这种情况下,多进程中的每个进程都提供一个线程。让我们理解以下示例。

示例 -

输出

Time taken in seconds - 3.3707828521728516

性能似乎有所提高,但进程管理有其自身的开销,并且多个进程比多个线程更重。

结论

在本教程中,我们讨论了 GIL 以及如何使用它。它允许单个线程一次执行。本教程还介绍了为什么 GIL 对 Python 程序员很重要。