为什么全球口译员锁?

Python的Global Interpreter Lock的function究竟是什么? 编译为字节码的其他语言是否采用类似的机制?

一般来说,对于任何线程安全问题,您将需要使用锁保护您的内部数据结构。 这可以通过不同级别的粒度来完成。

  • 您可以使用细粒度locking,每个单独的结构都有自己的锁。

  • 您可以使用粗粒locking,一个锁保护所有内容(GIL方法)。

每种方法都有各种各样的优点和缺点。 细粒度locking允许更高的并行性 – 两个线程可以在不共享任何资源时并行执行。 但是有一个更大的pipe理开销。 对于每行代码,您可能需要获取并释放多个锁。

粗粒度的方法是相反的。 两个线程不能同时运行,但是一个线程运行得更快,因为它没有做太多的簿记工作。 最终归结为单线程速度和并行性之间的折衷。

已经有一些尝试去除python中的GIL,但单线程机器的额外开销通常太大。 由于锁争用,即使在多处理器计算机上,某些情况实际上可能会变慢。

编译为字节码的其他语言是否采用类似的机制?

它有所不同,它可能不应该被视为一种语言财产,而是一个实施财产。 例如,有一些Python实现,如Jython和IronPython,它们使用底层VM的线程方法,而不是GIL方法。 此外,Ruby的下一个版本看起来正在推出一个GIL。

以下是官方的Python / C API参考手册 :

Python解释器不是完全线程安全的。 为了支持multithreading的Python程序,在安全地访问Python对象之前,必须由当前线程保存一个全局锁。 没有locking,即使是最简单的操作也可能导致multithreading程序出现问题:例如,当两个线程同时递增同一对象的引用计数时,引用计数可能最终只会增加一次而不是两次。

因此,只有获得全局解释器锁的线程才可以在Python对象上运行或调用Python / C API函数。 为了支持multithreading的Python程序,解释器会定期释放并重新获取锁 – 默认情况下,每100个字节码指令(这可以通过sys.setcheckinterval()来更改)。 该锁也被释放并重新获得,可能会阻止I / O操作(如读取或写入文件),以便其他线程可以在请求I / O的线程等待I / O操作完成时运行。

我认为这个问题总结得很好。

全局解释器锁是一个很大的互斥锁,可以保护引用计数器不被泄露。 如果你正在编写纯Python代码,这一切都发生在幕后,但是如果你将Pythonembedded到C中,那么你可能不得不明确地取/放锁。

这种机制与Python被编译为字节码无关。 Java不需要它。 实际上, Jython甚至不需要(python编译为jvm)。

也请看这个问题

Python和perl 5一样,并不是从头开始devise的。 线程被移植到事实之后,所以全局解释器锁被用来保持互斥,只有一个线程在给定的时间在解释器的肠子里执行代码。

每个Python线程都由解释器自己通过每隔一段时间循环一次锁进行多任务协作。

当你和其他Python线程处于激活状态来“join”这个协议并确保你的背后没有任何不安全的情况发生时,当你从C与Python交谈时,你需要自己去攫取锁。

其他具有单线程遗产的系统,后来演变为multithreading系统,通常具有这种机制。 例如,Linux内核在SMP早期就有“大内核锁”(Big Kernel Lock)。 随着multithreading性能成为问题,随着时间的推移,逐渐趋于尝试将这些types的锁分成更小的部分,或者尽可能地使用无锁algorithm和数据结构来取代,从而最大限度地提高吞吐量。

关于你的第二个问题,并不是所有的脚本语言都使用它,但是这只会使它们不那么强大。 例如,Ruby中的线程是绿色的而不是本地的。

在Python中,线程是本地的,GIL只能阻止它们在不同的核心上运行。

在Perl中,线程更糟。 他们只是复制整个解释器,远不如在Python中可用。

也许BDFL的这篇文章会有所帮助。