一个加班的夜晚
凌晨两点,办公室只剩你一个人。
屏幕上是一段爬虫代码,你要抓取一万个网页的数据。单线程跑了一个小时,才完成不到三分之一。你心想:Python不是支持多线程吗?开几个线程一起跑,速度不就翻倍了?
于是你花十分钟改成了多线程版本,信心满满地运行——
结果傻眼了:速度几乎没变。甚至,CPU占用率倒是上去了,但进度条还是慢得像蜗牛爬。
你开始怀疑人生:Python的多线程,是不是就是个摆设?
别急。你不是一个人遇到这个问题。无数Python开发者都在这条路上踩过坑——包括我自己。
今天咱们就掰开揉碎聊清楚:Python的多线程到底有没有用,什么时候有用,什么时候不仅没用反而更慢。
先讲个故事:GIL是个什么玩意儿
要理解多线程为什么有时候不灵,得先认识一个东西——GIL,全称叫全局解释器锁(Global Interpreter Lock)。
它是CPython(就是咱们平时用的那个Python解释器)里的一个机制。简单说就是:同一时刻,只有一个线程能执行Python的字节码。
什么意思呢?打个比方——
你开了一家奶茶店,店里只有一个操作台(这就是GIL)。你雇了10个员工(线程),但不管多少人,同一时间只能有一个人在操作台前面做奶茶。其他人要么在等,要么在干别的不需要操作台的活儿(比如招呼客人、打包)。
所以,就算你的电脑有8个CPU核心,跑Python多线程程序时,真正干Python代码活的,永远只有一个核心。
那问题来了:既然只能有一个线程执行代码,多线程还有什么意义?
答案是:看场景。
场景一:CPU密集型任务——开再多线程也没用
什么叫CPU密集型?就是那种大量消耗CPU计算资源的活儿,比如:
- 计算圆周率小数点后一亿位
- 训练一个机器学习模型
- 处理海量数据的排序和统计
- 图片滤镜处理
这些任务的特点是:CPU一直在吭哧吭哧算,基本不休息。
咱们做个实验。假设要计算1到100万之间的质数个数:
单线程跑完需要 28.63秒。
改成4个线程一起跑,你猜多久?
29.15秒。
不仅没快,反而慢了0.52秒。
为什么会这样?因为4个线程在抢同一个操作台(GIL) 。每个线程干一会儿就得让位给下一个,换来换去还要花时间(这叫上下文切换开销)。结果就是:并行没实现,开销倒增加了。
有开发者做过一个更直观的测试:单线程执行1亿次减操作耗时约6.5秒,两个线程各执行5千万次时,总耗时反而增加到6.8秒。多线程比单线程还慢——这跟直觉完全相反。
所以结论很扎心:对于纯计算型任务,Python的多线程确实是个摆设。
那CPU密集型任务想加速怎么办?用多进程(multiprocessing)。每个进程有自己的GIL,可以跑在不同的CPU核心上,真正实现并行。同样是上面的质数计算,4进程只需要 7.82秒,接近4倍提速。
场景二:IO密集型任务——多线程的神仙时刻
再来看另一种任务:IO密集型。
IO就是输入输出(Input/Output),包括:
- 网络请求(爬虫、调用API)
- 读写文件
- 数据库查询
这类任务的特点是:大部分时间在等。
你发一个网络请求,数据从服务器传回来需要几百毫秒。这段时间CPU是闲着的,啥也没干,就在那儿干等。
这时候多线程就派上用场了。
还是做实验。模拟20个网络请求,每个请求等1秒左右:
单线程顺序执行:5.12秒。
4个线程并发执行:1.28秒。
提速接近4倍。
为什么会这样?还记得GIL吗——那个只有一个操作台的奶茶店。
当一个线程发起网络请求后,它就在那儿等服务器回复。这时候它不需要操作台了。GIL就会自动释放,让其他线程上去用操作台。
等数据回来了,这个线程再重新获取GIL,继续处理结果。
换句话说:线程在等待IO的时候不占用GIL,其他线程可以趁机执行。这就实现了“伪并行”——虽然同一时刻只有一个线程在执行Python代码,但多个线程可以交替工作,把等待时间充分利用起来。
有开发者测试过网络请求场景,多线程的执行速度大约是单线程的2倍。另一个测试中,线程数增加到5倍时,速度提升约3.8倍,接近线性增长。
所以结论是:对于网络请求、文件读写这类IO密集型任务,Python的多线程非常有用。
那多进程呢?IO场景下反而更慢
你可能会想:既然多进程能绕过GIL,那IO密集型任务也用多进程不更好?
事实恰恰相反。
同样是上面那个20个网络请求的实验:
- 4线程:1.28秒
- 4进程:1.35秒
多进程反而更慢一点。
原因是:创建进程的开销远大于创建线程。进程有自己独立的内存空间,创建和销毁都要花更多资源。对于IO密集型任务,线程已经能很好地利用等待时间了,没必要用更重的进程。
一张图总结
| 任务类型 | 多线程 | 多进程 | 推荐方案 |
| CPU密集型(计算、运算) | ❌ 更慢 | ✅ 接近线性提速 | 多进程 |
| IO密集型(网络、文件、DB) | ✅ 大幅提速 | ⚠️ 略慢于多线程 | 多线程 |
实际工作中怎么选?
判断标准很简单:看你的程序是把时间花在“算”上,还是花在“等”上。
- 如果你的程序CPU一直100%满负荷运转——用多进程。
- 如果你的程序大部分时间在等网络响应、等磁盘读写——用多线程。
很多实际项目是混合型的。比如一个爬虫程序:
- 下载网页:IO密集型 → 用多线程
- 解析HTML:CPU密集型 → 用多进程
这时候可以把两者结合起来:用多进程池处理解析,用多线程池处理下载。
顺便提一句:Python 3.13的新变化
从Python 3.13开始,官方支持了一个叫自由线程(free-threading) 的构建模式,可以禁用GIL。
在这个模式下,多线程终于可以在CPU密集型任务上实现真正的并行。有测试显示,在M4 MacBook Air上实现了2.83倍的提速。
不过注意:目前这还是个实验性功能,默认并没有启用。在生产环境中,大部分项目用的还是带GIL的常规Python。所以理解GIL的规则、知道什么时候该用多线程什么时候该用多进程——依然是每个Python开发者的必修课。
回到开头那个故事
凌晨两点,你写的爬虫慢得像蜗牛。
现在你知道了:问题不在于“Python的多线程没用”,而在于你没搞清楚自己的任务类型。
如果你爬虫的瓶颈是网络请求的等待时间——开多线程,速度立竿见影。
如果你爬虫的瓶颈是解析网页的CPU计算——开再多线程也没用,改用多进程。
同样是多线程,用对场景是神器,用错场景是摆设。
这差距,真的离谱。