multiprocessing.pool是当前最稳妥、最易落地的多核并行方案,因其绕过gil实现真正多核并行,适用于纯python计算密集型任务;而threading受gil限制,仅适合io密集型场景。

直接结论:用 multiprocessing.Pool 是当前最稳妥、最易落地的多核并行方案,但必须避开序列化开销、进程数误设和阻塞等待这三类高频坑。
为什么不用 threading 而必须用 Pool?
CPython 的 GIL 会让所有线程在计算密集型任务中排队等锁——哪怕你开了 8 个线程,top 看到的也永远只有 1 个 CPU 核心在跑满。而 Pool 启动的是独立进程,每个都有自己的 GIL 和内存空间,能真正让 8 个核心同时干活。
注意:这不是“优化”,而是“绕过限制”。如果你的任务里混着大量 NumPy 计算(底层是 C),GIL 会自动释放,此时多线程也可能提速;但纯 Python 循环、字符串处理、自定义数值逻辑,一律走 Pool。
map vs imap_unordered:选哪个取决于你是否需要顺序结果
map 简单可靠,但会卡住主线程直到全部子任务完成,并按输入顺序返回结果。适合任务耗时接近、且下游依赖顺序的场景。
imap_unordered 更高效:哪个子进程先算完,就立刻 yield 结果,不等别人。内存占用低,适合流式消费或任务耗时差异大的情况(比如有的数据块大、有的小)。
快速生成专业的 Python 脚本和应用代码。一键创建完整项目结构,支持CLI、API、爬虫、Bot、Django等多种项目类型,包含完整的项目结构、配置文件、依赖管理、测试、README和文档。
- 用
map:要结果列表、不怕等、输入输出顺序必须一致 - 用
imap_unordered:想边算边处理、任务时间不可控、不想占太多内存 - 别用
imap:它保证顺序但仍是迭代器,性能不如imap_unordered,又没map直观,基本没优势
进程数设多少才不翻车?
设成 cpu_count() 是常见误区。物理核心数 ≠ 最佳进程数:
- CPU 密集型(如矩阵运算、模拟仿真):设为
os.cpu_count()或略少(比如减 1),避免调度抖动 - I/O 密集型(如批量请求+解析):可设为物理核数的 2–3 倍,因为进程常在等网络/磁盘,多开几个能填满空闲周期
- 内存吃紧时(如每任务加载大模型):宁可少开进程,否则
OSError: Cannot fork或 OOM 直接崩
实测建议:从 os.cpu_count() // 2 起步,逐步加,用 time.time() 对比总耗时,拐点之后再加反而变慢。
参数太大、函数不能 pickle 怎么办?
Pool 所有传参和返回值都靠 pickle 序列化,这是最隐蔽的性能杀手:
- 闭包函数、lambda、类实例方法默认不可 pickle → 改用模块级函数
- 传入超大 list/dict → 拆成 chunk,用
chunksize=参数控制每次送多少(比如pool.map(func, data, chunksize=100)) - 想共享只读数据(如配置、大数组)→ 用
initializer+ 全局变量,避免重复传输:def init_worker(shared_array): global arr arr = shared_array with Pool(4, initializer=init_worker, initargs=(big_array,)) as p: p.map(worker, tasks)
最后提醒一句:if __name__ == '__main__': 不是仪式感,是 Windows/macOS 上防止子进程反复 import 主模块导致无限 fork 的硬性要求——漏写就直接卡死或报错。
Python免费学习笔记(深入):立即使用
在学习笔记中,你将探索 Python 的核心概念和高级技巧!










