繁体   English   中英

为什么全球翻译锁定?

[英]Why the Global Interpreter Lock?

Python全局解释器锁的功能究竟是什么? 编译为字节码的其他语言是否采用类似的机制?

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

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

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

每种方法都有各种优缺点。 细粒度锁定允许更大的并行性 - 当两个线程不共享任何资源时,它们可以并行执行。 但是,管理开销会大得多。 对于每行代码,您可能需要获取并释放多个锁。

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

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

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

它有所不同,它可能不应被视为语言属性,而是实现属性。 例如,有一些Python实现,如Jython和IronPython,它们使用其底层VM的线程方法,而不是GIL方法。 此外,Ruby的下一个版本似乎正朝着引入GIL的方向发展。

以下内容来自官方Python / C API参考手册

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

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

我认为它很好地总结了这个问题。

全局解释器锁是一个很大的互斥锁,可以保护引用计数器免受冲击。 如果您正在编写纯Python代码,这一切都发生在幕后,但如果您将Python嵌入到C中,那么您可能必须明确地获取/释放锁。

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

另见这个问题

像perl 5一样,Python并不是从头开始设计的,而是线程安全的。 事实之后,线程被嫁接,因此全局解释器锁用于维持互斥,只有一个线程在解释器的内容中的给定时间执行代码。

单独的Python线程由解释器本身通过每隔一段时间循环锁定来协同多任务。

当你在C语言中与Python交谈时,当其他Python线程处于活动状态以“选择加入”此协议并确保在您的背后不会发生任何不安全时,就需要自己抓住锁。

具有单线程遗产的其他系统后来演变为多线程系统通常具有这种机制。 例如,Linux内核在早期的SMP时代就有“大内核锁”。 随着多线程性能成为一个问题,随着时间的推移逐渐出现,有一种趋势是尝试将这些类型的锁分解成更小的部分,或者用尽可能无锁的算法和数据结构替换它们以最大化吞吐量。

关于你的第二个问题,并非所有的脚本语言都使用它,但它只会降低它们的功能。 例如,Ruby中的线程是绿色的而不是本机的。

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

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

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

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM