multiprocessing 能真正提升 CPU 密集型任务性能,但需满足:任务纯计算无 I/O、输入可分片、函数可序列化、避免共享状态与大对象传递,并正确管理进程生命周期。

multiprocessing 能真正提升 CPU 密集型任务的性能,但前提是选对组件、避开常见陷阱——盲目套用反而拖慢程序。
什么时候必须用 multiprocessing 而不是 threading
只在任务明显卡在 CPU 计算上时才用:multiprocessing。比如跑 numpy 矩阵运算、遍历大列表做数值计算、调用纯 Python 的循环逻辑。
常见错误现象:用 threading.Thread 处理 100 万个数字求和,耗时几乎和单线程一样——这就是 GIL 在锁死线程并发。
- CPU 使用率持续 >80%,而 I/O 等待时间极短(可用
psutil.cpu_percent()和psutil.disk_io_counters()验证) - 函数内部不涉及太多文件读写、网络请求或数据库查询
- 输入数据可分片、结果可合并(如 map-reduce 模式)
Pool.map() 是最常用也最容易出错的入口
它适合“一批独立输入 → 一批独立输出”的场景,比如批量处理图像、计算多个独立区间的积分、解析一堆 JSON 文件。
立即学习“Python免费学习笔记(深入)”;
容易踩的坑:
- 传入的函数不能是嵌套函数或 lambda ——
Pool会 pickle 函数,而嵌套函数无法被序列化 - 默认使用
spawn启动方式(macOS/Linux)或spawn/fork(Linux),Windows 只支持spawn,所以务必保证if __name__ == "__main__":包裹主逻辑 - 不要在
map()里传巨大对象(如 GB 级 DataFrame)——进程间拷贝开销远超计算收益;应只传路径、ID 或轻量参数,让子进程自己加载
示例正确写法:
快速生成专业的 Python 脚本和应用代码。一键创建完整项目结构,支持CLI、API、爬虫、Bot、Django等多种项目类型,包含完整的项目结构、配置文件、依赖管理、测试、README和文档。
from multiprocessing import Pool import os <p>def process_chunk(chunk_id):</p><h1>子进程自己读数据,避免主进程传递大数据</h1><pre class="brush:php;toolbar:false;">data = load_data_by_id(chunk_id) # 实际中替换成 pd.read_csv 或类似 return data.sum()
if name == "main": chunk_ids = [1, 2, 3, 4] with Pool(os.cpu_count()) as pool: results = pool.map(process_chunk, chunk_ids)
Manager 共享状态时性能损耗比你想象的更大
当你需要多个进程往同一个列表追加结果、或动态更新计数器时,Manager.list() 或 Manager.dict() 看似方便,但背后是跨进程 RPC 调用,延迟高、吞吐低。
真实影响:
- 每调用一次
shared_list.append(x),实际触发一次 IPC(进程间通信),比内存操作慢 100 倍以上 - 不适合高频写入场景(如每毫秒更新一次的统计)
- 更适合“少量协调 + 大量独立计算”,比如记录各进程完成状态、汇总最终指标
更高效的做法:让每个进程返回局部结果,主进程最后合并。例如:
# ❌ 低效
shared = manager.list()
for i in range(4):
p = Process(target=worker, args=(shared,))
p.start()
<h1>✅ 推荐</h1><p>results = pool.map(worker_no_shared, task_list) # 各自返回 list,主进程 sum()
final_result = sum(results)
启动方式与资源泄漏风险
默认 Pool 或 Process 使用 spawn 启动,安全但启动稍慢;Linux 下可设 fork 加速,但有内存继承风险(如已初始化的数据库连接、全局缓存可能被复制后引发冲突)。
关键点:
- 永远用
with Pool() as pool:或显式调用pool.close(); pool.join(),否则子进程可能残留成僵尸进程 - 避免在子进程中再创建新
Pool(嵌套并行),极易触发资源竞争或递归爆炸 - 如果任务含 C 扩展(如
cv2、torch),注意某些库在spawn下需重新 import,否则报ModuleNotFoundError
复杂点在于:没有“通用最优配置”。CPU 核心数不等于最佳进程数——有时 os.cpu_count() - 1 更稳,尤其当机器还跑着其他服务时。


















