Python 中最广为人诟病的一点,大概就是它的 Global Interpreter Lock(简称GIL) 了。由于 GIL 的存在,Python 无法实现真正的多线程编程,因此很多人都把这视作 Python 最大的软肋。有些项目试图在没有GIL的情况下实现Python项目,甚至有些人直接从CPython中删除了GIL,GIL真的那么糟糕吗?
◆ ◆ ◆
什么是 GIL
GIL 即全局解释器锁(Global Interpreter Lock),是 Python 解释器中的一个布尔值,受到互斥保护。这个锁被 CPython 中的核心字节码用来评估循环,并调节用来执行语句的当前线程。
CPython 支持在单个解释器中使用多线程,但线程们必须获得 GIL 的使用权才能执行操作码(做低级操作)。这样做的好处是,Python 开发人员在编写异步代码或多线程代码时,完全不必操心如何获取变量上的锁,也不需担心进程因为死锁而崩溃。
GIL 使 Python 中的多线程编程变得简单。
当然,GIL意味着虽然 CPython 可以是多线程的,但在任何给定的时间里只能执行 1 个线程
◆ ◆ ◆
GIL比多核的概念要早
如上所述,Python不会在多个核上运行。这可能会让很多人惊讶,但请可以乃静下来想想,因为历史很重要。虽然Python可能听起来像一种新语言(它最近才变得流行),但它实际上是一种非常古老的语言,实际上比Java还早。截至现在已有29岁。
当python到达30岁时,也是Python 2被弃用的时间。然而具有多个CPU /核心的计算机是一个相当新的概念。长期以来,计算机制造商只是试图让CPU越来越小。在标准计算机中推出多个CPU是闻所未闻的。
多核处理器的创建完全是出于销售动机,因为CPU制造商正在努力使CPU的速度足够快以鼓励新的销售。为了解决这个问题,他们开始出货多核CPU,这基本上是两个CPU一起工作(嵌入在同一芯片上)。这样他们可以“加倍”CPU的速度,因为他们给了你两个。第一个多核处理器是在2000年初,也就是Python最初设计之后的10多年。AMD在2009年创建了第一个超过2核的处理器,这是在Python创建之后的19年,仅在10年前。
一旦双核CPU问世,人们就会意识到线程实际上可以在同一时间运行。真正的并行性,而不仅仅是并发性。虽然线程不是为了这个目的,但它们很好地发挥了作用,甚至不需要引入新的概念。
在线程世界中保护内存的核心概念在多核世界中也是如此。有些程序会在多核世界中出现错误,因为它们从未被编写为并行运行,但大多数情况下,只要您已经正确使用锁,一切都会正常工作。然而,Python和其他类似的语言,因为从来没有预料到可以这样,显得落伍了
◆ ◆ ◆
GIL的概念产生
现在不确定是哪种语言或人发明了GIL。也许是Python,也许不是。许多语言做了类似的事情,比如Python的GIL。Ruby和Javascript仍然具有某种形式的GIL,即使它们没有这样称呼它。它从何而来也许并不重要,重点是:Python决定使用GIL。
Python是一种旨在使原型设计和脚本编写简单易读的语言。“可读性优先”一直是Python中的设计原则。在所有处理上简单地拥有一个大锁,而不是强迫你管理自己的线程内存。
如果你在Python上进行多线程处理,你可能不会遇到代码中的竞争条件或死锁,甚至你也不必考虑锁。Python通过一个巨大的锁来锁定所有内容,为您完成所有工作。它不是很多微小的锁,这意味着对于多线程应用程序,Python代码更易于读取,编写和获取。这非常有趣。
当时,Python被认为是做得对的。Python开发人员喜欢这个。这是一个历史原因
◆ ◆ ◆
GIL仍然很棒?
GIL有很多好处。你不必考虑您的线程。Python中的东西大部分都是线程安全的。由于没有小的单独锁定,这意味着单线程应用程序比没有GIL时运行得更快。他们不必浪费时间无缘无故地锁定东西。它还允许Python的垃圾收集算法(引用计数)运行良好。
引用计数是一种非常有效的垃圾收集算法,它具有非常低的内存开销,并且不需要对应用程序进行任何“暂停”。这个算法的最大特点是它在多线程应用程序中不起作用,但只要你有一个全局锁,它就能正常工作。无论如何,当你想要的只是一个核心时,这种权衡是非常好的。
◆ ◆ ◆
仍然需要改变
我们已经确定GIL在单核应用中是有优势的。但现在已经9102年了,多核无处不在。为什么Python不适应时代?很大程度上是为了100%向后兼容。python官方也在做努力。
有一个Python项目,他们试图删除GIL,但是他们发现对于任何少于5个线程的东西,有GIL的时候速度反而快得多。GIL在某些情况下会是一种性能优化,删除它却获得了更差的性能将是一种愚蠢的做法。 在目前的实验中,只有在拥有大量内核和线程时才能看到性能提升。
这主要是由于垃圾回收算法以及代码已经设计为线程安全,是语言设计上的现状。为了改变这种状况,它需要改变语言的基本设计,从而影响到使用语言的每个人。 Python最后一次非向后兼容的更改(Python 2到Python 3)其实并没有太顺利,即使python3已经发布超过10年了,但人们仍然在大量使用Python2。
◆ ◆ ◆
解决办法
但是不用担心,仍有很多替代方法可以让多线程在Python中跨核心工作。首先GIL本身可以用Python中的c扩展来控制。这意味着您可以编写执行真正多处理的ac或rust库,并在Python代码中使用它,以使代码真正实现多核处理。像numpy这样的常见数据科学库。
另一种选择是使用协程等处理,在Python 3.8中,您将能够拥有一个共享内存对象(shared_memory),甚至你可以使用不同的Python实现,例如Jython和Stackless Python,它们没有GIL。虽然没有GIL,但这么多年了,jython仍然没有火起来,难度可想而知。
了解了GIL的历史和作用之后,也许你不会像以前那么讨厌它了,如果它真的影响到了你,试试以下几种办法:协程、多进程、jython、等python3.8修复。
◆ ◆ ◆
长按识别
关注 技术90分