为什么全球翻译锁定?

Fed*_*oni 86 python scripting multithreading locking bytecode

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

Bri*_*ian 69

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

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

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

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

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

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

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

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

  • @avi,像Web服务器这样的IO绑定进程仍然可以从Python线程中获益.两个或多个线程可以同时执行IO.它们不能同时解释(CPU). (3认同)
  • ^因此,在任何时间点,只有一个线程将向客户端提供内容...因此,实际使用多线程来提高性能毫无意义。对? (2认同)

Eli*_*sky 33

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

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

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

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


Dav*_*hme 19

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

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

另见这个问题

  • "这种机制与编译为字节码的Python无关":确切地说,它是CPython实现的工件.其他实现(如您所提到的Jython)可以通过其线程安全实现免除此限制 (4认同)

Edw*_*ETT 11

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

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

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

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

  • 当然.请注意我在回答问题后约3年.=) (5认同)
  • Linux有BKL,自版本2.6.39起,BKL已被完全删除. (3认同)

Eli*_*sky 7

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

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

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