Python GIL 与并发模型选型:多线程、多进程还是协程
【摘要】 一、GIL 是什么GIL(Global Interpreter Lock,全局解释器锁) 是 CPython 解释器里的一把互斥锁:同一时刻,一个 Python 进程内只有一个线程能执行 Python 字节码。很多人的第一反应是“那多线程不就没用了?”——并不完全对。要理解 GIL 的影响,得先分清任务类型:CPU 密集型 和 I/O 密集型。 二、GIL 为什么存在CPython 用引用...
一、GIL 是什么
GIL(Global Interpreter Lock,全局解释器锁) 是 CPython 解释器里的一把互斥锁:同一时刻,一个 Python 进程内只有一个线程能执行 Python 字节码。
很多人的第一反应是“那多线程不就没用了?”——并不完全对。要理解 GIL 的影响,得先分清任务类型:CPU 密集型 和 I/O 密集型。
二、GIL 为什么存在
CPython 用引用计数做内存管理,多线程同时改引用计数会出错,加锁是最简单的解法;同时 GIL 让单线程执行变快、C 扩展编写更简单。这是权衡的结果,不是“设计缺陷”。
代价就是:多线程无法真正并行跑 Python 字节码。
三、GIL 对两类任务的影响
CPU 密集型(计算、加密、图像处理……)
线程大部分时间在算,而 GIL 让它们只能轮流跑,多线程甚至因为切换开销比单线程还慢。多线程在此无收益。
I/O 密集型(网络请求、读写文件、数据库访问……)
线程在等待 I/O 时会主动释放 GIL,此时别的线程可以继续跑。所以多线程对 I/O 密集任务是有效的——等待时间被重叠了。
四、三种并发模型怎么选
| 模型 | 适用场景 | 关键点 |
|---|---|---|
| 多线程 threading | I/O 密集型 | 轻量、共享内存;受 GIL 限制不能并行计算 |
| 多进程 multiprocessing | CPU 密集型 | 每进程独立解释器和 GIL,真正并行;进程间通信/内存开销大 |
| 协程 asyncio | 高并发 I/O | 单线程事件循环,切换开销极小;适合海量连接 |
选择口诀:计算密集用多进程,I/O 密集用多线程或协程,海量并发连接优先协程。
五、代码对比
CPU 密集:多进程更快
from multiprocessing import Pool
def heavy(n):
return sum(i * i for i in range(n))
if __name__ == "__main__":
with Pool(4) as p: # 4 个进程,真并行
print(p.map(heavy, [10**7] * 4))
用 threading 跑同样的计算基本不会加速,因为 GIL 让它们串行。
I/O 密集:协程最省资源
import asyncio, aiohttp
async def fetch(session, url):
async with session.get(url) as resp:
return await resp.text()
async def main(urls):
async with aiohttp.ClientSession() as s:
return await asyncio.gather(*(fetch(s, u) for u in urls))
urls = ["https://example.com"] * 100
asyncio.run(main(urls)) # 100 个请求并发,单线程即可
协程在 I/O 等待时让出控制权,用极小的开销实现高并发。
六、绕过 GIL 的几种方式
- 多进程:最直接,进程级隔离,各有一把 GIL。
- C 扩展 / 原生库:NumPy、pandas 等底层是 C,计算时释放 GIL,能真正并行。
- 用别的解释器:Jython、IronPython 无 GIL;PyPy 有 STM 实验;CPython 3.13 起有实验性 free-threaded(无 GIL)构建,但生态尚在完善。
- 把重活丢给外部服务:如交给 C/Rust 服务、数据库、GPU 计算。
七、常见误区
- “Python 多线程完全没用”:错。I/O 密集型多线程依然有效。
- CPU 密集用多线程想加速:往往白忙,应换多进程。
- 协程里写阻塞调用:在
async函数里调用同步requests/time.sleep会卡死整个事件循环,要用异步库或run_in_executor。 - 多进程共享大对象:序列化/拷贝开销可能吃掉并行收益,注意数据传递成本。
- 线程数开太多:上下文切换和内存开销反噬性能,按 CPU 核数和任务类型合理设。
八、总结
GIL 决定了 CPython 单进程内多线程无法并行执行字节码,因此:
- CPU 密集型 → 多进程(或多用 C 扩展);
- I/O 密集型 → 多线程 / 协程;
- 海量并发 I/O → 协程(asyncio)。
理解任务类型是选对并发模型的前提。GIL 不是“Python 慢”的借口,选对模型、把重活交给能释放 GIL 的原生库,Python 一样能跑得又快又稳。
【声明】本内容来自华为云开发者社区博主,不代表华为云及华为云开发者社区的观点和立场。转载时必须标注文章的来源(华为云社区)、文章链接、文章作者等基本信息,否则作者和本社区有权追究责任。如果您发现本社区中有涉嫌抄袭的内容,欢迎发送邮件进行举报,并提供相关证据,一经查实,本社区将立刻删除涉嫌侵权内容,举报邮箱:
cloudbbs@huaweicloud.com
- 点赞
- 收藏
- 关注作者
评论(0)