1. 全局解释锁是什么
Global Interpreter Lock是计算机程序设计语言解释器用于同步线程的一种机制,它使得任何时刻仅有一个线程在执行。即使是在多核处理器上,使用GIL解释器也只允许同一时间执行一个线程。
常用的使用GIL的解释器有CPython与Ruby MRI。可见,GIL并不是python独有的特性。
2. Python解释器有哪些
- CPython
是官方版本的解释器,用C语言编写的,最广泛的解释器,可以方便的和C/C++进行交互 - Jpython
由java语言编写的python解释器 - Ipython
在交互效果上有所增强,执行方面与CPython一样的。 - PyPy
使用JIT技术的编译器,专注于执行速度,对Python代码进行动态编译。
3. CPython的线程不安全
Cpython的线程使用的是操作系统的原生线程。在Linux的pthread完全由操作系统调度执行。pthread本身不是线程安全的,需要使用者通过锁来实现多线程的安全运行。
因此CPython解释器下的Python实现多线程必然存在线程不安全的问题,这就为GIL在多核时代的使用埋下了隐患
4. 多核化对CPython的冲击
简单介绍下GIL运行原理
单核CPU情况
CPython的Phread是通过操作系统调度算法执行的。Python的解释器每执行一定数量的字节码,或遇到系统IO时,会强制释放GIL,然后触发一次操作系统的线程调度,实现单核CPU的充分利用,并且在单核释放和重新执行的时间间隔非常短
多核CPU情况
多核情况下多线程执行时,一个线程在CPU-A执行完之后释放GIL,其他CPU上的线程都会进行竞争,但CPU-A可能又马上获取到了GIL,这就导致其他CPU上被唤醒的线程只能眼巴巴地看着CPU-A上的线程再次执行,而自己只能等待,直到又被切换到待调度的状态,这就会产生多核CPU频繁进行线程切换,消耗着资源,但只有一个线程能够拿到GIL真正执行Python代码,这就导致多线程在多核CPU情况下,效率还不如单线程执行效率高。
这种情况非常类似于网络编程中的多个线程监听同一端口造成的惊群现象,只不过是CPU级别的,造成的浪费更加奢侈。
5. GIL的实际影响
1)I/O密集型
在单核CPU上执行多线程时由解释器实现了有效的切换,这一点是很有益处的。在I/O密集型的诸如网络爬虫等类型的程序即使使用GIL控制下的多线程程序性能也不会像你想象中那么糟糕。
2) CPU密集型
对于CPU密集型的计算类程序GIL就有比较大的问题,因为CPU密集型的程序本身没有太多等待,不需要解释器介入并且所有任务只能等待1个核心,其他核心空闲也无法使用,这么看对多核的使用确实很糟糕。
6. 优化
虽然后续的Python版本对于GIL进行了优化,但效果都不太好,目前在如下方面可以让程序拥抱多核:
1)多进程
Python2.6引入了MultiProcess库来弥补Threading库中GIL带来的缺陷,基于此开发多进程程序,每个进程有单独的GIL,避免多进程之间对GIL的竞争,从而实现多核的利用,但是也带来一些同步和通信问题,这也是必然会出现的。
2)Ctypes
CPython的优势就是与C模块的结合,因此可以借助Ctypes调用C的动态库来实现将计算转移,C动态库没有GIL可以实现对多核的利用。
3)协程
协程也是一个很好的手段,在Python3.4之前,官方没有对协程的支持,存在一些三方库的实现,比如gevent和Tornado。3.4之后就内置了asyncio标准库,官方真正实现了协程这一特性。
参考如下文章:
https://zhuanlan.zhihu.com/p/87307313