全局解释器锁(GIL)
Guido van Rossum 的注释”This is the GIL“ 添加于2003 年,但这个锁本身可以追溯到1997年他的第一个多线程 Python 解释器。
在 Unix系统中,PyThread_type_lock 是标准 C mutex_t 锁的别名。当 Python 解释器启动时初始化。
解释器中的所有 C 代码在执行 Python 时必须保持这个锁。Guido 最初加这个锁是因为它使用起来简单。
而且每次从 CPython 中去除 GIL 的尝试会耗费单线程程序太多性能,尽管去除 GIL 会带来多线程程序性能的提升,但仍是不值得的。(前者是Guido最为关切的, 也是不去除 GIL 最重要的原因, 一个简单的尝试是在1999年, 最终的结果是导致单线程的程序速度下降了几乎2倍.)
GIL 对程序中线程的影响足够简单,你可以在手背上写下这个原则:“一个线程运行 Python ,而其他 N 个睡眠或者等待 I/O.”(即保证同一时刻只有一个线程对共享资源进行存取) Python 线程也可以等待threading.Lock或者线程模块中的其他同步对象;线程处于这种状态也称之为”睡眠“。
一个线程无论何时开始睡眠或等待网络 I/O,其他线程总有机会获取 GIL 执行 Python 代码。这是协同式多任务处理。CPython 还有抢占式多任务处理。
如果一个线程不间断地在 Python 2 中运行 1000 字节码指令,或者不间断地在 Python 3 运行15 毫秒,那么它便会放弃 GIL,而其他线程可以运行。把这想象成有多个线程但只有一个 CPU 的时间片。
协同式多任务处理
当一项任务启动,而在较长的或不确定的时间内,没有运行任何 Python 代码的需要,一个线程便会让出GIL,从而其他线程可以获取 GIL 而运行 Python。这种礼貌行为称为协同式多任务处理,它允许并发;多个线程同时等待不同事件。在同样的时间内它们可以做更多的工作。
抢占式多任务处理
- Python线程可以主动释放 GIL,也可以先发制人抓取 GIL 。
Python 的程序分两个阶段运行。
- 首先,Python文本被编译成一个字节码的简单二进制格式。
- 第二,Python解释器的主回路,一个名叫 pyeval_evalframeex() 的函数,流畅地读取字节码,逐个执行其中的指令。
- 当解释器通过字节码时,它会定期放弃GIL,而不需要经过正在执行代码的线程允许,这样其他线程便能运行。
如果一个线程可能随时失去 GIL,你必须使线程安全。
许多 Python 操作是原子的。不会被解释器打断夺走 GIL 。
- 尽管 GIL 不能免除我们加锁的需要,但它确实意味着没有加细粒度的锁的需要(所谓细粒度是指程序员需要自行加、解锁来保证线程安全, 而 CPthon 中是粗粒度的锁,即语言层面本身维护着一个全局的锁机制,用来保证线程安全)。因为在 Python 中线程无法并行运行,细粒度锁没有任何优势。
总结:
- Python的GIL在单核情况下对性能的影响可以忽略不计,几乎没有。
- Python由于其GIL的存在,在多核CPU的情况下Thread的表现真的是非常的糟糕,但是Process则不受GIL的影响。
- Python内置的数据类是不适合用于大量的数学计算的,当然这也不仅仅是Python的问题,其它完全面向对象的语言都有这个问题, 要进行大量的数学计算就要把代码移到C/C++中去实现,这样不仅可以去除GIL的影响,更可以让性能获得几十倍上百倍的提升, 或者用numpy之类的扩展库在执行科学计算时也可以让性能大幅的提升。
- Python慢其实就是慢在数字计算上,想想就知道,如果每一个数字都是一个对象, 在计算的时候就免不了不断的为对象申请内存,释放内存,速度肯定就慢下来。
- Python对数据结构的操作是非常高效的,像Python内置的强大的dict,str,list等类型,其处理的速度真的可以和C媲美,因为它们的实现本身就是用C实现的。 我们在编程刚入门的时候就被告知:数据结构+算法=程序,这个道理也许只会在用Python这样的语言时才会有更切身的体会。
- 在用Python开发程序时,你不得不花点时间放在性能优化上来, 过程也很简单:用cProfile类查找出比较耗时的操作,然后将其移到C中去实现, 另外,如果是使用多核CPU的情况,一定要小心使用Thread,尽量用Process来替代Thread,通过本文对GIL的分析,将对性能的优化提供很好的帮助。 其实,Python的性能优化过程也是程序开发中有挑战又非常有成就感的部分。
- 但是,记住一点,不要过早的对程序进行优化,过早优化是罪恶之源 —Donald Knuth。前期开发应该把注意力放在功能实现以及代码的可读性和可维护性上来。
参考:
1.https://blog.csdn.net/I2Cbus/article/details/23555063
2.http://python.jobbole.com/87743/