什么是 GIL ?
首先GIL (全局解释锁)
不是Python的特性,而是解释器CPtyhton的特性。GIL
简言之是一个互斥锁,只允许一个线程控制 Python 解释器,也就是说,但任一时间点都只有一个线程处于执行状态。
为什么要设计GIL?
为什么要有GIL
呢,或者说GIL
是为了解决什么问题呢?
Python使用引用计数来管理内存,也就是说Python中创建的对象有一个引用计数变量,该变量跟踪指向该对象的引用数量,当该计数到0时,对象占用的内存被释放。显然,该引用计数不能同时被两个线程同时增加或减少值。否则将可能导致内存泄漏(永远不会被释放)或不正确地释放(引用仍然存在)。
通过给线程间共享的所有数据结构加锁可以保证引用计数变量的安全性。但是这又导致一些问题:如果为每个对象添加锁就会导致多个锁的存在,有可能会导致死锁;再就是重复地获取和释放所有会降低性能。
于是出现了GIL
,GIL
是解释器本身的锁,所有的 Python 获取 GIL
才能执行,这可以防止死锁并且不会带来太多心能开小。但它使得程序编程单线程。
要注明的是,GIL
不是解决该问题的唯一方法。
为什么选择 GIL呢?
首先,Python诞生的时候操作系统没有线程的概念;Python 的许多扩展使用 C 写的,而 C 扩展需要 GIL
提供的线程安全内存管理;GIL
易于实现,易于添加到 Python 中。
GIL 的影响 CPU 密集型和 IO 密集型
GIL锁的释放
- 协同式多任务处理:IO操作,在较长的或者不确定的时间,没有运行Python代码的需要,线程便会让出GIL;
- 抢占式多任务处理:对于CPU密集型的的程序,解释器运行一段时间就会放弃GIL,而不需要经过正在执行代码的线程允许,这样其他线程便能运行。(在python3中,这个时间间隔是15毫秒)
任务可以分为 CPU密集型
和 IO密集型
。 CPU密集型
的特点是要进行大量的计算,消耗CPU资源,而 IO 密集型涉及网络、磁盘等IO操作,CPU消耗较小,任务的大部分时间都在等待IO操作完成。
GIL
对两种任务的影响是不同的,对于完全CPU密集型任务,GIL
不仅会把多线程变成多线程,还会导致用时比单线程更长,因为涉及了锁的获取及释放;而IO密集型任务的性能则不会有太大影响。
为什么没有移除 GIL
有人做过实验,移除GIL
使用更小粒度的锁,发现单线程性能上降低非常严重,多线程程序也只有线程数达到一定数量时才有性能上的改进。