线程的就绪状态:当位于新建状态的线程调用 start() 方法后,该线程就转换到就绪状态。所谓就绪,就是告诉 CPU,该线程已经可以实行了,但是详细什么时候实行,取决于 CPU 什么时候调度它。换句话说,如果一个线程处于就绪状态,只能说明此线程已经做好了准备,随时等待 CPU 调度实行,并不是说实行了 start() 方法此线程就会立即被实行。
线程的运行状态:当位于就绪状态的线程得到了 CPU,并开始实行 target 参数实行的目标函数或者 run() 方法,就表明当火线程处于运行状态。但如果当前有多个线程处于就绪状态(等待 CPU 调度)时,处于运行状态的线程将无法一直霸占 CPU 资源,为了使别的线程也有实行的机会,CPU 会在肯定时间内强制当前运行的线程让出 CPU 资源,以供其他线程利用。**
线程的阻塞状态:当 CPU 对多个线程进行调度时,对于得到 CPU 调度却没有实行完毕的线程,就会进入阻塞状态。现在几乎所有的桌面和服务器操作系统,都采用的是抢占式优先级调度策略。即** CPU 会给每一个就绪线程一段固定时间来处置惩罚任务,当该时间用完后,系统就会阻止该线程继续利用 CPU 资源,让其他线程得到实行的机会**。对于详细选择那个线程上 CPU,不同的平台采用不同的算法,比如先进先出算法(FIFO)、时间片轮转算法、优先级算法等,每种算法各有优缺点,实用于不同的场景。
线程死亡状态:对于得到 CPU 调度却未实行完毕的线程,它会转入阻塞状态,待条件成熟之后继续转入就绪状态,重复争取 CPU 资源,直到其实行结束。实行结束的线程将处于死亡状态。线程实行结束,除了正常实行结束外,如果步调实行过程发生异常(Exception)或者错误(Error),线程也会进入死亡状态。
GIL,全称是 Global Interpreter Lock,也叫做全局解释器锁。对于 CPython,所有的 Python 线程都需要在解释器这个假造机中运行,而在运行之前都要先获取 GIL 这个锁,然后每实行 100 个字节码,解释器就自动开释 GIL 锁,让别的线程有机会实行。因此即使你有多个 CPU 核,多个线程在同一个 Python 假造机中也应该是交替实行的。 这就意味着:同一时间,只能有一个线程在实行的状态。GIL 对单线程步调没有影响,但会成为 CPU 麋集和多线程代码的性能瓶颈。即使在多线程布局的代码中,在同一时刻 GIL 也只允许一个线程在实行状态,因此,GIL 成为了 Python 不受欢迎的一个特性。
CPU 麋集型步调是指步调运行过程中 CPU 是性能瓶颈,该类步调会涉及大量数学计算,例如矩阵乘法/搜索/图像处置惩罚等。I/O 麋集型步调是指步调耗费了大部分时间来等待 I/O 事故,I/O 事故大概来自用户/文件/数据库/网络等。在从数据源获取到文件之前,I/O 麋集型步调需要等待大量的时间;因为在 I/O 事故就绪前,数据源需要进行自己的处置惩罚过程。例如:用户耗费时间思考向输入提示符输入什么内容(会占用时间),或者数据库在接收到检索哀求后运行自己的步调(会占用时间)。
解决 GIL 的方法包括利用多进程、利用其他 Python 解释器或利用 C 扩展模块等。
2.4 生产者-消耗者模子