跳过正文
  1. 技术杂项/

Python 中的 GIL

·209 字·1 分钟
目录

简单叙述
#

GIL 是 CPython(C 语言实现的 Python 解释器) 中一种用来实现多线程的机制,英文全称为 Global Interpreter Lock.

历史缘由
#

CPython 是 Python 官方实现的解释器,也是目前使用最广泛的 Python
解释器。GIL 作为 CPython 中一个非常关键的机制,它的作用和局限性我们会在后面的详细论述。而追究起其存在的历史,应该说 GIL 机制是在 CPython 实现 Python 程序多线程的设计之初就已经存在的。至于采用 GIL 机制的缘由,官方文档里并没有详细说,只是提到,GIL 存在的必要性与 CPyhton 的内存管理机制是非线程安全的有主要关系(T_his lock is necessary mainly because CPython’s memory management is not thread-safe_)。而从 Jython(Java 语言实现的 Python 解释器)的官方文档里我们还可以得到一些蛛丝马迹:

Jython does not have the straightjacket of the GIL. This is because all Python threads are mapped to Java threads and use standard Java garbage collection support (the main reason for the GIL in CPython is because of the reference counting GC system).

看来,GIL 机制的使用,还与 CPython 的另一非常重要的机制——垃圾回收机制有关。CPython 在实现 Python 的垃圾回收机制的时候,使用了一种叫引用计数(Reference Counting)的方法。而 CPython 之所以采用了 GIL 这样一种效率低下的方法,就是由于引用计数这种垃圾回收机制的限制。

  • 引用计数

    我们在创建一个对象的时候,可以把它理解为内存中的一个地址,而有多少个变量指向了这个地址,就可以理解为该对象有多少个引用,当这个地址没有任何变量指向它,即该对象没有任何引用的时候,便可以认定它已经是存在与内存中但不可能被用到的垃圾,就像是C语言中的野指针,再也不可能被后面的程序正常访问到,因此解释器就把其当作垃圾,而将该块内存回收了。

    引用计数的优点:

    1、实现简单

    2、实时性强:

    一旦没有引用,内存立马就被释放了。

    引用计数的缺点:

    1、维护引用计数消耗资源

    2、循环引用缺陷:

    比如两个对象互相引用,则引用计数永远不为零,所占用的内存也永远无法被回收。

机理和作用
#

GIL(Global Interpreter Lock) 按字面意思来理解,就是解释器的一把全局锁。解释器中的任何一个线程 如果想要操作 Python 对象,就必须要先获得这把锁,以防止多个线程的 Python 代码同时被执行。而 CPython 的内存管理机制的非线程安全性,决定了 GIL 机制存在的必然。如果没有这把锁的限制,即使是最简单的操作,并行线程也可能导致灾难性的错误。比如说,当两个线程同时增加某一对象的引用计数,并发操作的话,可能最终这个对象的引用计数只增加了一次,而非预期的两次。

因而,GIL 机制的存在保证了只有获得了全局锁的线程能够操作 Python 对象或者调用 Python/C API. 而为了保证线程的并行,解释器定期地在各个线程间切换。

解释器还保存了一些线程相关的信息在一个名为 PyThreadState 的数据结构中。同时还有一个指向 PyThreadState 的全局变量,可以通过PyThreadState_Get() 获得。

缺点
#

GIL 机制因其使得 Python 程序无法真正发挥出多线程的性能优势而被人所诟病。

某些场景下,在多核硬件上,两个线程调用同一个函数,与单线程调用该函数两次相比,前者的耗时不是后者的一半,也并非与后者持平,而是后者的近两倍!而单核设备上,这个差距能缩小一点,但仍是前者与后者有着不小的差距。这看起来很令人费解,下面这个链接详细解释了这个问题。

  • 参考链接:

没有 GIL 的 Python 解释器
#

如前所述,GIL 是 CPython 中的一个特殊机制,并非所有 Python 解释器中都存在。JythonIronPython 解释器中就没有 GIL 机制,并且能够充分发挥多线程的性能优势。

因为 GIL 是 Python 官方解释器中的机制,以及其历史的悠久性,解释器中其他的很多特性都是基于该机制的,大量的 Python/C API 依赖于该特性,因此 CPython 的去 GIL 化是一个非常困难繁杂的工作。目前,该工作只是静静地躺在 Python 开发者的邮件列表中,偶尔被人提及,而没有人去认领。

Getting rid of the GIL is an occasional topic on the python-dev mailing list. No one has managed it yet.