Python多线程在CPU密集型任务中无法并行,因GIL强制串行执行字节码;IO密集型任务不受影响,因系统调用时GIL自动释放;正确解法是CPU密集型用multiprocessing、IO密集型用asyncio、混合任务选用可释放GIL的C扩展。

Python多线程在CPU密集型任务里根本不能并行,不是写法问题,是GIL强制串行执行字节码。
为什么GIL会让4个线程跑得比1个还慢
CPython用引用计数管理内存,多个线程同时改ob_refcnt会引发崩溃。GIL就是为锁住这块内存操作而生的——它不区分任务类型,只要执行Python字节码,就必须抢这把锁。
常见错误现象:top看到CPU始终卡在100%,time.time()测出4线程耗时比单线程长5%–20%。
- GIL不是“偶尔释放一下”,而是“每次执行约5ms字节码后强制让出”,对纯计算来说,这只是增加切换开销,毫无并发收益
- 线程数越多,锁竞争越激烈,
pthread_mutex_lock/unlock调用本身就开始吃CPU - 所有线程挤在同一个核上跑,其余核心全程闲置,
os.cpu_count()返回8也白搭
为什么IO密集型任务却不受影响
GIL在遇到系统调用时会自动释放,比如socket.recv()、open().read()、time.sleep(),甚至requests.get()底层的select()。
立即学习“Python免费学习笔记(深入)”;
这时线程挂起,GIL交出,其他线程立刻接管——你感觉“并发”,其实是“快速轮流等待”。
- 不是GIL变松了,是你的代码主动让出了执行权
- 一旦IO返回,线程重新争抢GIL;若紧接着又是一段长循环计算,GIL马上被锁死,后续线程继续干等
- 混合任务(如读文件→解析JSON→发请求)要小心:解析JSON若用纯Python实现,那段就卡GIL;换成
ujson或orjson,它们在C层释放GIL,就能并发加速
别指望绕过GIL,优先换执行模型
试图用threading.Lock或调整sys.setswitchinterval()来“优化”GIL,基本是徒劳。GIL是解释器级机制,应用层无法禁用或削弱。
真正有效的路径很明确:
- CPU密集型:直接切到
multiprocessing,用concurrent.futures.ProcessPoolExecutor替换ThreadPoolExecutor,接口几乎一致 - 高并发IO(如爬虫、API网关):用
asyncio+aiohttp,单线程撑几千连接,无GIL争抢,内存占用低 - 计算中夹着C扩展:确认你用的库是否释放GIL,比如
numpy.dot()、cv2.threshold()都做了,但自己写的cython函数若没加nogil:块,照样卡住
最容易被忽略的一点:GIL不等于线程安全
很多人以为“有GIL就不用加锁”,这是危险误解。GIL只保证字节码原子性,而counter += 1会被编译成LOAD、INCR、STORE三步,两个线程交错执行仍会导致结果丢失。
共享变量该用threading.Lock还是得用,该用queue.Queue还是得用——GIL从不替你管业务逻辑的竞态。



















