Python 代码的执行是由 Python 虚拟机(又名解释器主循环)进行控制的。Python 在设计时是这样考虑的,在主循环中同时只能有一个控制线程在执行,就像单核 CPU 系统中的多进程一样。内存中可以有许多程序,但是在任意给定时刻只能有一个程序在运行。同理,尽管 Python 解释器中可以运行多个线程,但是在任意给定时刻只有一个线程会被
解释器执行。
对 Python 虚拟机的访问是由全局解释器锁(GIL)控制的。这个锁就是用来保证同时只能有一个线程运行的。在多线程环境中,Python 虚拟机将按照下面所述的方式执行。
- 设置 GIL
- 切换进一个线程去运行
- 执行下面操作之一
3.1 指定数量的字节码指令
3.2 线程主动让出控制权(可以调用 time.sleep(0)来完成) - 把线程设置回睡眠状态(切换出线程)。
- 解锁 GIL
- 重复上述步骤。
当调用外部代码(即,任意 C/C++扩展的内置函数)时,GIL 会保持锁定,直至函数执行结束(因为在这期间没有 Python 字节码计数)。编写扩展函数的程序员有能力解锁 GIL,然而,作为 Python 开发者,并不需要担心 Python 代码会在这些情况下被锁住。
例如,对于任意面向 I/O 的 Python 例程(调用了内置的操作系统 C 代码的那种),GIL 会在 I/O 调用前被释放,以允许其他线程在 I/O 执行的时候运行。而对于那些没有太多 I/O 操作的代码而言,更倾向于在该线程整个时间片内始终占有处理器(和 GIL)。换句话说就是,I/O 密集型的 Python 程序要比计算密集型的代码能够更好地利用多线程 环境。