浅析CPython的全局解释锁GIL
1.全局解释锁的定义
2.Python的解释器
CPython
Jython
IronPython
IPython
PyPy
3.CPython的线程不安全
4.GIL产生的背景和面临的挑战
Python是Guido van Rossum 在1989年发布的,那个时候计算机的主频还没有达到1G,程序全部都是运行在单核计算机上面,直到2005年多核处理器才被Intel开发出来。
Python各版本发布时间轴:
多核化对软件系统的冲击
多核化对CPython的冲击
图为Python之父--人称"仁慈的独裁者"的吉多.范罗苏姆:
但是随着多核时代的到来,高效地利用CPU 核心的有效方法就是使用并行性,多线程是充分实现并行的好方法,但是CPython的GIL却阻碍了对多核CPU的利用。
痛并快乐着的GIL
CPython的GIL给使用者带来了便利,并且在GIL的基础上开发了许多重要的Package和语言功能。但是多核CPU的普适和其他语言对Python的冲击,让GIL显得原始而粗暴,无法有效利用多核处理器成为了弊端。
5.多核时代GIL暴露的问题
单核CPU情况
多核CPU情况
6.GIL的实际影响
I/O密集型
CPU密集型
GIL一直备受争议,为此PEP也多次尝试删除或者优化GIL,但是解释器本身的复杂性和众多GIL下的类库都让GIL移除成为遥不可及的想法。
移除GIL
在1999年针对Python 1.5,一个free threading补丁已经尝试实现了这个想法,该补丁来自Greg Stein。在这个补丁中,GIL被完全的移除,且用细粒度的锁来代替。然而,GIL的移除给单线程程序的执行速度带来了一定的代价。当用单线程执行时,速度大约降低了40%。使用两个线程展示出了在速度上的提高,但除了这个提高,这个收益并没有随着核数的增加而线性增长。由于执行速度的降低,这一补丁被拒绝了,并且几乎被人遗忘。
1999年多核还是个幻想,但是在现今移除GIL也异常困难,真的移除效果如何也是未知的,只能说回头太难。
优化GIL
2009年Antoine Pitrou 在Python 3.2中实现了一个新的GIL,并且带着一些积极的结果。这是GIL的一次最主要改变,旧的GIL通过对Python指令进行计数来确定何时放弃GIL。单条Python指令将会包含大量的工作。在新的GIL实现中,用一个固定的超时时间来指示当前的线程以放弃这个锁,使得线程间的切换更加可预测。
python作为生命力极强的热门语言,绝对不会在多核时代坐以待毙。即便有GIL的限制,仍然有许多方法让程序拥抱多核。
多进程:Python2.6引入了MultiProcess库来弥补Threading库中GIL带来的缺陷,基于此开发多进程程序,每个进程有单独的GIL,避免多进程之间对GIL的竞争,从而实现多核的利用,但是也带来一些同步和通信问题,这也是必然会出现的。
Ctypes:CPython的优势就是与C模块的结合,因此可以借助Ctypes调用C的动态库来实现将计算转移,C动态库没有GIL可以实现对多核的利用。
协程:协程也是一个很好的手段,在Python3.4之前,官方没有对协程的支持,存在一些三方库的实现,比如gevent和Tornado。3.4之后就内置了asyncio标准库,官方真正实现了协程这一特性。
GIL仍然是Python语言里最困难的技术挑战,GIL问题的并不是编程语言的本身问题,换做其他语言只是将问题转移到了用户层面,相反Python的作者尝试将这种问题转移到解释器给使用者呈现一个优雅的语言。
虽然多核时代的到来暴露了GIL的缺陷,但是Python决策者和社区开发者已经做出了许多其他措施来拥抱多核,无知地诟病GIL是不明智的做法。
如同生产关系要适应生产力的发展一样,抛开历史背景谈机制的优劣,都是有失偏颇的,所以对待GIL要辩证看待。
Done is better than Perfect.
10.参考资料:
https://juejin.im/post/59af5087518825244e6cdf39
https://wiki.python.org/moin/GlobalInterpreterLock
https://mail.python.org/pipermail/python-dev/2009-October/093321.html
http://kaito-kidd.com/2018/04/26/python-advance-global-interpreter-lock/
https://juejin.im/entry/5bb094c7e51d450e8b13dff3