GIL是什么?
全局解释器锁 (GIL) 是CPython解释器(官方、最常用的 Python 解释器)中的一个互斥锁 (mutex)。它的核心作用是:在任意时刻,只允许一个线程执行 Python 字节码 。
这意味着,即使你的计算机有多个 CPU核心,一个 CPython 进程中的多个线程也无法真正地并行执行 Python 代码。它们可以并发执行(即交替执行),但不能在同一瞬间并行运行。
GIL为什么存在?
GIL 的存在主要是为了简化 CPython 的内存管理机制。
CPython 使用引用计数 (Reference Counting) 来进行自动内存管理。每个 Python 对象都有一个与之关联的引用计数器,当这个计数器变为 0 时,对象占用的内存就会被回收。
如果没有 GIL,在多核系统上,两个线程可能同时尝试增加或减少同一个对象的引用计数。这会造成竞态条件 (Race Condition) ,导致引用计数出错,从而引发两种严重问题:
1. 内存泄漏 :对象的引用计数永远不会归零,导致其占用的内存永远无法被回收。
2. 程序崩溃 :过早地释放了仍在被引用的对象,当其他代码再次尝试访问这个已被释放的内存时,程序就会崩溃。
GIL 通过确保任何时候只有一个线程能操作 Python 对象,从根本上解决了这个问题。这使得 CPython 的内存管理变得简单和安全,也极大地简化了 C 语言扩展(C Extension)的开发,因为开发者不需要自己处理复杂的线程安全问题。
GIL带来的影响
对 CPU 密集型任务的限制不友好。对于大量需要计算的任务,多线程无法利用多核CPU的优势来提升性能。因为一直只有一个线程在执行真正的计算。
对 I/O 密集型任务友好:对于大部分时间都在等待输入/输出(I/O)操作的任务(例如,文件读写、网络请求、数据库查询),GIL 的影响不大。当一个线程执行 I/O 操作时,它会释放 GIL ,让其他线程有机会执行。因此,在等待网络响应的同时,另一个线程可以处理数据,从而提高了程序的整体效率。
如何绕过 GIL 的限制?
1. 使用 multiprocessing 模块 :
multiprocessing 模块通过创建多个独立的进程来替代线程。每个进程都有自己的 Python 解释器和内存空间,因此每个进程都有自己的 GIL,它们之间互不干扰,可以被操作系统调度到不同的 CPU 核心上并行执行。
2. 使用其他 Python 解释器 :
1. Jython :运行在 Java 虚拟机 (JVM) 上,没有 GIL。
2.IronPython :运行在 .NET 平台上,没有 GIL。
这些解释器使用其底层平台的垃圾回收机制,因此不需要 GIL。但它们的缺点是可能与一些 C 语言编写的 Python 库不兼容。
3. 使用 C 扩展 :
对于计算密集的部分,可以将其用 C/C++/Rust 等语言编写成 Python 扩展。在执行这些外部代码时,可以手动释放 GIL,从而允许其他 Python 线程运行。像 NumPy、Pandas、SciPy 这样的科学计算库就是通过这种方式实现了高性能的并行计算。