Python并发到底该用多线程还是多进程?
作者:🧑🚀 deadmau5v 发布于 2025/9/16
Python 的并发编程,经常让人头大。
选 threading?听说有 GIL 锁。选 multiprocessing?进程开销又太大。选 asyncio?代码写起来像天书。
本文一次性讲清楚:到底该用哪个?
性能对比(越长越好)
核心痛点:GIL
GIL(Global Interpreter Lock)本质上是一把互斥锁,用来保护 CPython 解释器的内存管理(引用计数)不乱套。它保证同一时刻只有一个线程在跑 Python 代码。
这意味着:如果是 CPU 密集型任务(算数、图像处理),多线程不仅没用,反而因为线程切换比单线程还慢。
三种模式怎么选
1. 多线程 (threading)
基于操作系统线程,会被 GIL 锁住。适合 I/O 密集型任务(爬虫、读写文件),因为 I/O 等待时 GIL 会自动释放,其他线程可以接着跑。
2. 多进程 (multiprocessing)
每个进程都有独立的解释器和 GIL,真正的并行。但启动慢,内存占用大。适合 CPU 密集型任务(科学计算、视频转码)。
3. 协程 (asyncio)
单线程里的用户态线程,没有系统切换开销,快到飞起。适合高并发 I/O 场景(Web 服务器、高频爬虫)。
最佳实践:混合双打
真实世界里,往往既要算数,又要联网。
方案:asyncio 管 IO,ProcessPoolExecutor 管计算。
import asyncio
from concurrent.futures import ProcessPoolExecutor
# CPU 任务:跑在子进程
def heavy_calculation(num):
return sum(i * i for i in range(num))
async def main():
loop = asyncio.get_running_loop()
# 1. 创建进程池(复用进程,别老 Fork)
with ProcessPoolExecutor() as pool:
# 2. 把计算扔给子进程,主线程继续跑 await
task = loop.run_in_executor(pool, heavy_calculation, 10**7)
# 3. 主线程顺手做点 IO
print("一边算数,一边联网...")
await asyncio.sleep(1)
# 4. 收工
result = await task
print(f"算完了: {result}")
if __name__ == "__main__":
asyncio.run(main())
关于 No-GIL Python (PEP 703)
Python 3.13 已经开始实验移除 GIL 了。但在它普及之前(起码还得几年),上面的结论依然是金科玉律。
小结
写爬虫或 Web 服务无脑选 asyncio(FastAPI、Aiohttp)。算数据必须用 multiprocessing,或者调用 C 库(NumPy 自动释放 GIL)。写脚本用 ThreadPoolExecutor 最简单。
评论