首页
学习
活动
专区
圈层
工具
发布
社区首页 >专栏 >Python 的 GIL 到底卡住了啥?为什么多线程跑不快,3 个实验看清真相

Python 的 GIL 到底卡住了啥?为什么多线程跑不快,3 个实验看清真相

原创
作者头像
小小张说故事
发布于 2026-10-09 11:15:15
发布于 2026-10-09 11:15:15
500
举报

系列:Python 底层设计逻辑|类型:原理拆解型|分发:CSDN / 博客园 / 腾讯云 字数:约 1600 字

结论先行:GIL(全局解释器锁)是 CPython 里一把"全局锁",保证同一时刻只有一个线程在跑 Python 字节码。它锁的是"解释器执行权",不是你的变量。所以 CPU 密集型任务用多线程基本白搭——多个线程轮流抢这把锁,反而有切换开销;但 I/O 密集型任务(网络、文件)不受影响,因为等待时会释放 GIL。想真并行算,得上多进程或把计算丢给 C 扩展。


1. 先破除一个误解:GIL 锁的不是你的数据

很多人以为 GIL 是防止多线程改同一个变量。错。保护变量靠的是你自己的锁(threading.Lock)。GIL 锁的是解释器本身——CPython 的内存管理(引用计数)不是线程安全的,所以干脆让同一时刻只有一个线程能执行字节码,从根本上避开竞争。

代码语言:python
复制
import threading
x = 0
def inc():
    global x
    for _ in range(100000):
        x += 1
# 两个线程各加 10 万次,结果却常常不是 200000

关键:上例结果不对,是因为 x += 1 不是原子操作,GIL 会在中间切换——这跟 GIL"防竞争"没关系,恰恰说明 GIL 不保护你的数据,你得自己加锁。

2. 实验一:CPU 密集型,多线程 vs 多进程

代码语言:python
复制
import time, threading, multiprocessing

def cpu_task():
    n = 0
    for _ in range(10_000_000):
        n += 1

# 多线程
t0 = time.time()
ts = [threading.Thread(target=cpu_task) for _ in range(4)]
[t.start() for t in ts]; [t.join() for t in ts]
print("线程:", time.time() - t0)

# 多进程
t0 = time.time()
ps = [multiprocessing.Process(target=cpu_task) for _ in range(4)]
[p.start() for p in ps]; [p.join() for p in ps]
print("进程:", time.time() - t0)

现象:四核机器上,多进程明显比多线程快接近一倍,多线程几乎没加速。原因就是 GIL——四个线程挤一把锁,谁也别想真并行算。

3. 实验二:I/O 密集型,多线程反而香

代码语言:python
复制
import time, threading, requests

def io_task():
    requests.get("https://www.baidu.com")   # 网络等待

t0 = time.time()
ts = [threading.Thread(target=io_task) for _ in range(10)]
[t.start() for t in ts]; [t.join() for t in ts]
print("IO 线程:", time.time() - t0)

为什么:网络请求时线程在等,CPython 会在等待点释放 GIL,别的线程就能跑。所以 I/O 密集场景多线程完全能打,这也是 Web 服务爱用多线程的原因。

4. 实验三:GIL 在 Python 3 里其实会变脸

Python 3.2 之后 GIL 改用"超时切换":一个线程默认跑 5 毫秒就主动让出,避免单线程霸占。你可以用 sys.setswitchinterval 调这个值观察行为变化。但别指望它让 CPU 任务变快——切换更公平,不等于能并行。

5. 那到底怎么绕开 GIL?

  • CPU 密集:用 multiprocessing 开多进程,每个进程独立 GIL、独立解释器,真并行。(代价是进程间通信贵。)
  • 计算下沉 C:NumPy、Pandas 的底层是 C,计算时释放 GIL,所以向量化操作不受 GIL 拖累。
  • 异步 asyncio:单线程并发处理海量 I/O,本就不依赖多线程。
  • 换解释器:PyPy、Jython 没有 CPython 这种 GIL;官方也在做"nogil"实验分支。

对照表:什么时候 GIL 是事,什么时候不是

场景

多线程有效吗

原因

纯计算(循环/数学)

❌ 几乎无效

抢同一把 GIL,无法并行

网络/文件 I/O

✅ 有效

等待时释放 GIL

NumPy 向量化计算

✅ 有效

底层 C 释放 GIL

多进程

✅ 真并行

每进程一把独立 GIL


排错清单(收藏级)

  • "我开了 8 个线程为啥 CPU 只跑满 1 核":CPU 密集任务被 GIL 卡住,改多进程。
  • 多线程改同一变量结果错:GIL 不保护你的数据,该加 Lock 还得加。
  • Web 服务用多线程没问题:因为瓶颈在 I/O,GIL 早释放了。
  • 想加速数值计算:优先向量化(NumPy),别盲目堆线程。
  • 别神话多进程:进程开销大、不能共享内存,轻量并发还是线程/协程。

一句话记住:GIL 锁的是解释器的"执行权",不是你的数据。CPU 活儿上进程,I/O 活儿随便多线程——先分清瓶颈在哪,再决定怎么绕。

原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。

如有侵权,请联系 cloudcommunity@tencent.com 删除。

目录
  • 1. 先破除一个误解:GIL 锁的不是你的数据
  • 2. 实验一:CPU 密集型,多线程 vs 多进程
  • 3. 实验二:I/O 密集型,多线程反而香
  • 4. 实验三:GIL 在 Python 3 里其实会变脸
  • 5. 那到底怎么绕开 GIL?
  • 对照表:什么时候 GIL 是事,什么时候不是
  • 排错清单(收藏级)
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档