为什么有全局解释器锁?
Python的全局解释器锁(Global Interpreter Lock,简称GIL)到底是干什么的呢?它的作用是确保在任何时候,只有一个线程可以执行Python字节码。这就意味着即使你的程序有多个线程,实际上也只能一个一个地运行,不能同时进行。这种设计是为了避免多个线程同时访问Python对象时可能出现的冲突和错误。
那么,其他一些编译成字节码的语言是否也有类似的机制呢?其实有些语言也会采用类似的方式来管理线程,确保在多线程环境下的安全性和稳定性。不过每种语言的实现方式和具体细节可能会有所不同。
5 个回答
以下内容摘自官方的Python/C API参考手册:
Python解释器并不是完全线程安全的。为了支持多线程的Python程序,当前线程在安全访问Python对象之前,必须先获得一个全局锁。如果没有这个锁,即使是最简单的操作在多线程程序中也可能出问题:比如,当两个线程同时增加同一个对象的引用计数时,引用计数可能只会增加一次,而不是两次。
因此,有个规则就是,只有获得了全局解释器锁的线程才能操作Python对象或调用Python/C API函数。为了支持多线程的Python程序,解释器会定期释放并重新获得这个锁——默认情况下,每执行100条字节码指令就会这样做(这个可以通过sys.setcheckinterval()来改变)。在进行可能会阻塞的输入输出操作,比如读写文件时,锁也会被释放和重新获得,这样其他线程就可以在请求输入输出的线程等待操作完成时继续运行。
我觉得这段话很好地总结了这个问题。
一般来说,解决线程安全问题时,你需要用锁来保护你的内部数据结构。这可以通过不同的方式来实现。
你可以使用细粒度锁定,也就是每个独立的数据结构都有自己的锁。
你也可以使用粗粒度锁定,也就是一个锁保护所有东西(这就是全局解释器锁,简称GIL的做法)。
这两种方法各有优缺点。细粒度锁定允许更高的并行性——当两个线程不共享任何资源时,它们可以同时执行。不过,这样的管理开销会大得多。每一行代码可能都需要获取和释放多个锁。
而粗粒度锁定则正好相反。两个线程不能同时运行,但单个线程会运行得更快,因为它不需要处理那么多的管理工作。最终,这就是在单线程速度和并行性之间的权衡。
在Python中,有几次尝试去去掉GIL,但对于单线程机器来说,额外的开销通常太大了。在某些情况下,即使在多处理器机器上,由于锁的竞争,性能反而会更慢。
其他编译成字节码的语言是否也采用类似的机制?
这要看情况,可能不应该把它当作语言的特性,而更应该看作是实现的特性。例如,有些Python的实现,比如Jython和IronPython,使用的是它们底层虚拟机的线程处理方式,而不是GIL的方式。此外,Ruby的下一个版本似乎也在朝着引入GIL的方向发展。