Python 全局解释器锁(GIL)

通过这一节的学习,希望大家能够理解下面几个问题:

  1. Python 全局解释器锁到底是什么?
  2. Python 全局解释器锁如何影响多线程?
  3. Python 全局解释器锁的应用场景?

1. GIL 的影响

在讲解之前,我们先要理解一点:Python 解释器本身是一个 C 程序,Python 代码是由这个 C 程序来解释执行的。

当我们在 Python 解释器中用多线程的方式实现多任务时,Python 解释器程序内部就会创建两个系统线程,分别去解释执行 Python 的函数代码。

这看起来一切都正常,可问题来了。由于一些历史原因,Python 解释器内部的两个线程在执行多个 Python 函数代码时,存在资源竞争问题。

Python 解释器内部创建的线程在执行一个 Python 函数代码时,需要读写解释器内部的一些全局数据。此时,如果另外一个线程也在读写这些全局数据,就会导致 Python 代码执行异常。

所以,Python 解释器内部的线程,不得已在每次执行 Python 函数代码时都要先加上互斥锁,防止自己正在使用的全局数据被意外篡改,导致任务函数执行异常。

举个例子。当 Python 解释器的线程执行任务函数 1 时,会对共享资源加锁,等函数执行完毕、或者发生 IO 阻塞等待时,才会释放这把锁。与此同时,由于全局数据被加了锁,线程 2 就拿不到全局数据的使用权,也就没办法执行任务函数 2。

至此你会发现,由于这把锁的存在,多线程并不能真正实现多个任务函数并行,而是交替执行。这个互斥锁,就是所谓的全局解释器锁(GIL)。

2. GIL 应用场景

Python 的多线程无法真正实现并行,也就无法利用多核 CPU 资源,看起来很鸡肋。那么,Python 的多线程真的就一无是处了吗?

并不是。对于那些在执行过程中频繁需要等待的 IO 密集型任务,多线程还是有一定优势的。前面我们提到,当线程碰到 IO 操作时,会自动释放锁,让其他任务函数执行,避免浪费 CPU 的时间资源。

举个例子。当线程 1 执行的 Python 代码中碰到 IO 操作,就会马上释放锁,相当于放弃这次的执行。线程 2 就会马上获得锁、获得执行机会,而不是在那里默默等待,浪费 CPU 时间。

此外,如果你的程序是需要长时间连续计算的计算密集型任务,建议使用多进程。因为多进程不受全局解释器锁的影响,能够充分利用多核 CPU 资源来提升计算效率。

多进程的本质,是创建多个独立的解释器来执行多个 Python 函数代码。

最后,我们来总结一下:

  1. Python 全局解释器锁(Global Interpreter Lock,GIL)是一个存在于解释器中的互斥锁。
  2. 由于全局解释器锁的存在,Python 多线程无法实现真正的并行计算。
  3. 对于 IO 密集型任务,Python 多线程具有一定的优势。
  4. 对于 CPU 密集型任务,Python 多进程更加适合。

注意:Python 全局解释器锁只存在于官方的 CPython 解释器中。

至此,关于全局解释器锁就讲解完毕,希望对你有所帮助。