免费编程软件「pythonpycharm」链接https://pan.quark.cn/s/48a86be2fdc0一个想优化性能、结果越优化越慢的故事去年有个朋友跟我吐槽说他想用多线程加速一个数据处理任务。原代码是单线程跑的处理100万条数据要8秒。他觉得CPU利用率太低心想我有8个核开8个线程岂不是1秒就能跑完于是他兴冲冲地改了代码把数据平均分给8个线程并行处理。结果跑完一看傻眼了——耗时11秒比单线程还慢了3秒。他发来一段代码给我看import threading import time def cpu_task(n): 纯计算任务数到n total 0 for i in range(n): total i return total # 单线程跑 start time.time() for _ in range(4): cpu_task(100_000_000) print(f单线程: {time.time() - start:.2f}秒) # 多线程跑 threads [] start time.time() for _ in range(4): t threading.Thread(targetcpu_task, args(100_000_000,)) threads.append(t) t.start() for t in threads: t.join() print(f多线程: {time.time() - start:.2f}秒)输出结果单线程: 8.42秒 多线程: 11.26秒多线程反而更慢了。他问我Python的多线程是不是假的为什么8个线程还不如1个线程我说不是假的但你遇到了Python最臭名昭著的那个坑——GIL全局解释器锁。GIL是个什么东西GIL全称是Global Interpreter Lock是CPython就是咱们平时用的那个Python解释器里的一个互斥锁。它的作用是确保同一时刻只有一个线程能执行Python字节码。你可以把它想象成一个令牌哪个线程拿到了令牌才能执行代码。执行一会儿比如执行100条字节码指令就把令牌放下其他线程抢到后再接着执行。听起来挺公平的对吧但这有个致命问题在多核CPU上多个核心明明是空闲的但因为GIL的存在它们只能干瞪眼看着一个线程在跑。这就是Python多线程在计算密集型任务上表现糟糕的根本原因。为什么GIL拖累了计算任务我再用一个餐厅比喻来解释单线程一个厨师干活。虽然慢但没有空闲时间。多线程8个厨师但厨房里只有1把炒锅GIL。谁拿到锅谁才能炒菜。拿到锅的厨师炒一会儿执行一些字节码必须放下锅让给下一个人。结果就是8个厨师大部分时间在抢锅和等锅真正炒菜的时间并没有增加反而因为抢来抢去浪费了时间。这就是多线程比单线程还慢的原因——线程切换和锁竞争的开销抵消了多核并行带来的好处。网上有人实测过单线程跑5亿次循环耗时8.4秒两个线程各跑2.5亿次总耗时11.2秒反而慢了。我用上面的代码复现结果完全一致。GIL对哪些任务影响最大CPU密集型任务——也就是那些大量消耗CPU计算资源的代码比如循环累加、数学计算、图像处理、机器学习训练等。GIL对这类任务的性能打击最严重。那为什么Python还要保留GIL因为GIL的诞生最初是为了简化内存管理。CPython用引用计数来管理内存如果没有GIL多个线程同时修改同一个对象的引用计数会导致计数错误进而引发内存泄漏或崩溃。加上GIL就安全了代价就是性能。等等那多线程在Python里还有用吗有用。而且非常有用。GIL的坑主要体现在CPU密集型任务上。对于I/O密集型任务多线程依然能带来巨大的性能提升。什么是I/O密集型任务就是那些大部分时间在等待外部资源比如等待网络响应、等待磁盘读写的任务。比如网络爬虫等待服务器返回数据文件读写等待磁盘数据库查询等待数据库返回结果在这些场景下线程在等待I/O完成时会主动释放GIL让其他线程有机会执行。所以你可以同时发起几十个网络请求并发地等待响应效率远高于串行一个接一个地等。破解GIL的三大方案如果你的任务确实是CPU密集型的又有性能要求有以下几条路可以走方案1用多进程multiprocessing既然GIL限制了线程那就直接绕过它——用进程。每个进程有自己独立的Python解释器和内存空间也都有自己的GIL所以能真正并行地利用多核CPU。from multiprocessing import Pool def cpu_task(n): total 0 for i in range(n): total i return total if __name__ __main__: with Pool(4) as pool: results pool.map(cpu_task, [100_000_000] * 4)缺点进程间通信开销大数据共享麻烦。方案2把计算密集部分下沉到C扩展对于numpy、pandas这类底层用C/C写的库它们在执行计算时会释放GIL不受这个限制。所以用numpy做矩阵运算多线程确实是能加速的。方案3异步编程asyncio对于I/O密集型任务asyncio可以做到在单线程内高并发地处理大量I/O操作效率比多线程更高而且没有GIL的烦恼。好消息Python正在移除GILPython 3.13已经提供了实验性的无GIL构建版本自由线程Free-Threaded。在Python 3.14中这个特性进一步得到了完善。根据一些测试对于数据并行、互相独立的工作负载无GIL版本可以把执行时间缩短到原来的1/4能耗也大幅降低。有开发者实测某个任务从5.77秒缩短到了1.36秒。但要注意无GIL版本目前还不是默认选项而且有代价单线程性能会略微下降内存占用会增加约10%因为引入了更细粒度的锁机制第三方库的兼容性还在逐步完善中Python之父Guido van Rossum也提醒过不要神话并发。很多人并行化之后反而变慢了与其追逐热点不如把代码的可维护性和长期演进放在更优先的位置。记住这个坑以后别再踩了回到开头那个朋友的问题我给他的建议很简单先判断你的任务是CPU密集型还是I/O密集型。I/O密集型放心用threading它会帮你提速。CPU密集型用multiprocessing或者换用numpy这类底层用C实现的库。如果用纯Python硬算开再多线程也没用甚至更慢。GIL是CPython的一个历史性设计决策它让Python的内存管理变得简单安全但也付出了性能代价。理解了它你就能在Python的并发编程里少走很多弯路。另外随着无GIL版本的逐步成熟未来这个坑可能会越来越浅。但在那之前记住今天的结论Python多线程在计算密集型任务上确实会比单线程还慢。