Python 多进程与多线程性能对比

8次阅读

多进程适合CPU密集型任务,因GIL限制线程无法提升python的CPU性能;I/O密集型可用Threading或asyncio;混合场景应拆分处理,避免GIL与阻塞互相拖累。

Python 多进程与多线程性能对比

多进程适合 CPU 密集型任务,多线程在 Python 里几乎不提升 CPU 性能

这是由 GIL(全局解释器锁)决定的:CPython 中同一时刻只有一个线程能执行 Python 字节码。所以即使开了 10 个 threading.Thread 做数值计算,实际仍是串行执行,还多了线程切换开销。

常见错误现象:time.time() 测出来多线程比单线程还慢;top 或任务管理器显示 CPU 占用率始终卡在 100%(单核),而不是接近 N×100%。

适用场景:

  • CPU 密集型(如科学计算、图像处理、加密解密)→ 用 multiprocessing
  • I/O 密集型(如 http 请求、文件读写、数据库查询)→ threadingasyncio 都可,前者更简单

multiprocessing.Pool 比手动创建 Process 更省心也更高效

直接用 Process 启动几十个子进程容易失控:进程生命周期难管理、结果收集要自己搞共享内存或队列、异常传播困难。而 Pool 封装了进程复用、任务分发、结果聚合和错误回传。

立即学习Python免费学习笔记(深入)”;

实操建议:

  • pool.map(func, iterable) 替代循环调用 Process(target=func, args=(x,))
  • 注意 iterable 会被序列化(pickle),不能传 Lambda、嵌套函数、带绑定方法的对象
  • 默认进程数是 os.cpu_count(),CPU 密集任务别盲目设更大值,反而因调度开销拖慢整体

线程池 concurrent.futures.ThreadPoolExecutor 是 I/O 任务的首选

相比原始 threading 模块,它自动管理线程生命周期、支持 as_completed 和超时控制,且 API 与 ProcessPoolExecutor 一致,方便后续替换为多进程。

典型误用:

  • ThreadPoolExecutor 跑纯计算(比如 sum([i**2 for i in range(10**6)]))→ 白忙活,GIL 锁死
  • 忘记调用 shutdown(wait=True) 或没用 with 上下文 → 程序可能提前退出,任务被丢弃
  • 提交太多任务到线程池(比如 10000 个 HTTP 请求)→ 不是线程越多越快,通常 10–50 个线程就足够压满网络带宽

混合场景:I/O + CPU 拆开做,别让 GIL 和阻塞互相拖累

比如“下载图片 → 缩略图处理”这种组合任务,不能全扔给线程(缩略图卡 GIL),也不能全扔给进程(下载部分启动开销大、上下文切换重)。

推荐做法:

  • ThreadPoolExecutor 下载一批图片(I/O 并发
  • 把下载完的本地路径列表交给 ProcessPoolExecutor 批量做缩略图(CPU 并发)
  • 避免跨进程传递原始图片二进制数据,改传文件路径——减少 pickle 开销和内存拷贝

最容易被忽略的是:子进程无法继承主线程的 TLS(线程局部存储)、数据库连接、日志 handler 等资源,初始化必须放在子进程内部,不能靠父进程传入。

text=ZqhQzanResources