为什么全球翻译锁定?

时间:2008-11-05 16:23:48

标签: python multithreading scripting locking bytecode

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

6 个答案:

答案 0 :(得分:68)

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

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

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

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

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

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

  

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

它有所不同,它可能不应被视为语言属性,而是实现属性。 例如,有一些Python实现,如Jython和IronPython,它们使用其底层VM的线程方法,而不是GIL方法。此外,下一版本的Ruby看起来正在移动towards引入GIL。

答案 1 :(得分:33)

以下内容来自official Python/C API Reference Manual

  

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

     

因此,该规则仅存在   已获得的线程   全局解释器锁可以操作   Python对象或调用Python / C API   功能。为了支持   多线程Python程序,   口译员定期发布和   重新获取锁定 - 默认情况下,   每100字节码指令(这个   可以改变   sys.setcheckinterval())。锁是   也发布并重新获得了   可能会阻止I / O操作   喜欢读或写文件,所以   其他线程可以在运行时运行   请求I / O的线程是   等待I / O操作   完整。

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

答案 2 :(得分:19)

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

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

另见this question

答案 3 :(得分:11)

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

解释器本身通过循环锁定来协调地多任务处理单个Python线程。

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

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

答案 4 :(得分:7)

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

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

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

答案 5 :(得分:2)

BDFL的this文章可能会有所帮助。