穿透 Python GIL 的迷雾:它到底锁的是什么?为什么 CPython 仍···

发布时间:2025-12-22 08:30:15 作者:cxyx 来源:本站 浏览量(5) 点赞(8)
摘要:穿透 Python GIL 的迷雾:它到底锁的是什么?为什么 CPython 仍坚持多线程?在我这些年教授 Python、参与高性能系统开发的经历中,最常被问到的问题之一就是:“GIL 到底锁的是什么?是不是锁住了所有线程?”“既然 GIL 会阻塞多线程并行,那 CPython 为什么还要保留多线程?”“多线程在 Python 中还有意义吗?”这些问题

穿透 Python GIL 的迷雾:它到底锁的是什么?为什么 CPython 仍坚持多线程?

在我这些年教授 Python、参与高性能系统开发的经历中,最常被问到的问题之一就是:


“GIL 到底锁的是什么?是不是锁住了所有线程?”


“既然 GIL 会阻塞多线程并行,那 CPython 为什么还要保留多线程?”


“多线程在 Python 中还有意义吗?”

这些问题背后,是 Python 开发者对性能、并发和底层机制的深深困惑。GIL(Global Interpreter Lock,全局解释器锁)几乎是 Python 世界里最具争议的设计之一,它既是历史遗留,又是现实妥协;既是性能瓶颈,又是稳定基石。

这篇文章,我将带你从 Python 的发展背景讲起,逐步深入 GIL 的本质、触发机制、设计原因与工程实践价值,并结合大量代码示例与真实案例,帮助你真正理解:


 GIL 到底锁住了什么
✅ 为什么 CPython 仍然保留多线程
✅ 多线程在 Python 中的正确使用方式
✅ 如何绕过 GIL 获得真正的并行

让我们从头开始。

一、开篇:Python 的发展与 GIL 的诞生

Python 自 1991 年诞生以来,以“简洁、优雅、可读性强”著称。它的设计哲学之一是:

让开发者专注于业务逻辑,而不是内存管理与线程安全。

在 Python 的早期版本中,内存管理依赖引用计数(Reference Counting)。引用计数的更新不是原子操作,因此在多线程环境下会产生竞争条件(Race Condition)。


为了避免复杂的锁设计、提升解释器稳定性、降低开发成本,Python 的作者 Guido van Rossum 做了一个关键决定:

用一个全局锁保护解释器内部状态。


这就是 GIL 的起源。


二、GIL 到底锁的是什么?(核心问题)

很多人以为 GIL 锁住的是:

  • 所有线程

  • 所有 Python 对象

  • 所有 CPU 核心

但这都是误解。

✅ GIL 锁住的不是 Python 对象,而是整个解释器状态(Interpreter State)

更准确地说:


GIL 锁住的是 CPython 的字节码执行器(Bytecode Interpreter)。


也就是说:

  • 只有持有 GIL 的线程才能执行 Python 字节码

  • 其他线程即使被调度,也无法执行 Python 层面的运算

  • GIL 不锁 I/O,不锁 C 扩展,不锁系统调用


GIL 的本质:保护引用计数与对象模型

CPython 的对象模型不是线程安全的:

  • 引用计数不是原子操作

  • 内存分配器不是线程安全的

  • 对象内部状态不是线程安全的

因此:

GIL 是 CPython 为了保证对象模型一致性而加的全局互斥锁。


三、GIL 的工作机制:什么时候会释放?什么时候会阻塞?

GIL 的行为可以总结为:

image.png

但 GIL 并不是一直不放,它会在以下情况释放:

1. I/O 操作时释放 GIL(最重要)

例如:

image.png


time.sleep() 会释放 GIL,因此多个线程可以并发执行。

这也是为什么:

✅ Python 多线程适合 I/O 密集型任务
❌ 不适合 CPU 密集型任务

✅ 2. C 扩展主动释放 GIL

例如 NumPy

image.png

NumPy 的矩阵运算在 C 层面执行,不受 GIL 限制,可以真正并行。

✅ 3. 线程切换时释放 GIL(每隔一定字节码执行次数)

Python 3.2 之前:

  • 每执行 100 条字节码 切换一次线程

Python 3.2 之后:

  • 改为基于时间片(默认 5ms)


四、为什么 CPython 仍然保留多线程?

这是很多人最困惑的问题:

“既然 GIL 限制了并行,那 Python 为什么还要多线程?”

答案有三个层面。

✅ 1. 多线程对 I/O 密集型任务非常有效

例如:

  • 网络请求

  • 文件读写

  • 数据库访问

  • 爬虫

  • 日志处理

这些操作会释放 GIL,因此多线程可以真正并发。

示例:

image.png

多线程可以显著提升吞吐量。

✅ 2. 多线程比多进程更轻量

  • 线程共享内存

  • 创建成本低

  • 上下文切换快

  • 适合高并发 I/O

在 Web 服务中,多线程仍然是主流模型之一。

✅ 3. CPython 的生态大量依赖线程

例如:

  • logging 模块内部使用线程

  • asyncio 的事件循环使用线程池

  • concurrent.futures.ThreadPoolExecutor

  • 各种 C 扩展内部使用线程

如果移除线程,整个生态将崩溃。

五、GIL 的误区:你以为它限制了所有并行,其实没有

以下情况不受 GIL 限制:

✅ 1. I/O 操作(会释放 GIL)

例如:

  • 网络请求

  • 文件读写

  • sleep

  • socket

  • subprocess

✅ 2. C 扩展(主动释放 GIL)

例如:

  • NumPy

  • Pandas

  • TensorFlow

  • PyTorch

  • Pillow

这些库内部使用 C/C++,可以真正多核并行。

✅ 3. 多进程(完

image.png

每个进程有自己的 GIL,因此可以真正并行。

二维码

扫一扫,关注我们

感兴趣吗?

欢迎联系我们,我们愿意为您解答任何有关网站疑难问题!

您身边的【网站建设专家】

搜索千万次不如咨询1次

主营项目:网站建设,手机网站,响应式网站,SEO优化,小程序开发,版权登记,商标注册等

立即咨询 0535-8953057