global interpreter lock,即python为了保护线程安全而采取的独立线程运行的限制。就是一个内核在一个时间点只能运行一个线程,对于io密集型任务,多线程能起到作用,对于cpu密集型,对线程就无能无力,反而还会因为争夺资源而变慢。
要理解GIL,就要从python的底层说起。
c++这样的语言是编译型语言,指程序输入到编译器,编译器再根据语言的语法进行解析,然后翻译成语言独立的中间表示,最后链接成具有高度优化的机器码的可执行程序,编译器可以看到整个程序,或者一大块独立的部分,从而进行深层次的代码优化。以及可以对不同的语言指令之间的交互进行推理,从而给出更有效的优化手段。
与此相反,Python是解释型语言,程序被输入的解释器来运行,解释器在程序运行之前对其并不了解,它所知道仅是python的规则,以及在执行过程中如果动态的应用这些规则。它也有一些优化,但是是有别的编译型语言优化的另一种优化,无法深入底层对程序进行推到,python的大部分优化都是解释器自身的优化,和所写的程序没有什么关系。Python程序的执行速度直接与解释器的速度相关。
问题的关键是,要想利用多核系统,Python必须支持多线程运行。作为解释型语言,Python的解释器必须做到既安全又高效,解释器需要的做的是避免在不同的线程中操作内部共享的数据,同时它还要保证在管理用户线程时保证总是最大化的计算资源。
那么不同线程访问时,数据的保护机制是什么样的?这就是GIL,全局解释器锁。这是加在解释器上锁,来保证数据的安全,诸如对当前线程状态和垃圾回收而用的堆分配对象这样的东西的访问提供保护。GIL 会通过字节码行数以及时间片释放,遇到io操作会主动释放。这种方式很安全,但是它最大的缺点时,对于任何python程序,不管有多少的处理器,任何时候都总是只有一个线程在执行。
解决GIL的一些尝试。添加细粒度的锁来阻止多个对象的同时访问,对于对线程这种方法是有效的,但是这个收益并没有随着核数的增加而线形增加。且在单线程时,速度大概降低40%,因此这种方法逐渐被遗忘。对于其他的python解释器,已经有很多不使用GIL,但是cpython支持的库是最多的。
我们希望的是在移除GIL时,并且对于单线程下的python代码没有带来性能的下降,最想要的是一个线程API能利用所有的处理器。但是多线程的开发和调试是单线程的数倍,多线程没有一个非常成熟的应用。
要想了解GIL的实现,需要对操作系统设计,多线程编程,C语言,解释器设计和Cpython解释器的实现有着非常彻底的理解。
目前的解决办法就是多进程和下面的协程。协程虽然也是单cpu,但是能减小切换代价而提升性能。