multiprocessing比threading更适合CPU密集型自动化脚本,因GIL使threading无法并行计算,而multiprocessing可真正利用多核;但进程开销大,单次耗时>100ms且无强共享状态时才优选。

为什么 multiprocessing 比 threading 更适合 CPU 密集型自动化脚本
Python 的 threading 在 CPU 密集任务中几乎不提速,因为 GIL 会强制串行执行。自动化脚本如果涉及大量计算、文件解析、图像处理或数据转换,必须用 multiprocessing 才能真正利用多核。但注意:进程启动开销比线程大,短任务(如单次 HTTP 请求)反而可能更慢。
- 判断标准:
time.perf_counter()测单次任务耗时 > 100ms,且无强共享状态需求 → 优先选multiprocessing - 避免把全局变量直接传给子进程——它们不会被自动同步,改了也没用
- Windows 下必须将进程启动逻辑包裹在
if __name__ == "__main__":中,否则会无限 fork
用 multiprocessing.Pool 快速并行化循环任务
最常见场景是批量处理文件、调用 API 或跑独立测试用例。不用手动管理进程生命周期,Pool 自动分配和回收。
- 默认进程数 = CPU 核心数,显式指定用
processes=4(避免超载) -
pool.map()保证输入顺序输出顺序一致;pool.imap()流式返回,内存友好但顺序不保 - 函数必须可序列化(不能是 lambda 或嵌套函数),建议定义在模块顶层
def process_file(filepath):
# 假设这是耗时操作
with open(filepath, "r") as f:
return len(f.read())
<p>if <strong>name</strong> == "<strong>main</strong>":
files = ["a.txt", "b.txt", "c.txt"]
with multiprocessing.Pool(processes=3) as pool:
results = pool.map(process_file, files) # 同步阻塞,等全部完成
如何安全传递参数和捕获子进程异常
multiprocessing 中子进程崩溃默认静默失败,主进程可能卡死或拿到 None,必须主动处理。
- 用
pool.apply_async()+.get(timeout=...)可设超时并捕获multiprocessing.TimeoutError或子进程抛出的异常 - 复杂参数(如含文件句柄、数据库连接)不要直接传——序列化失败。改用路径、ID 或配置字典
- 日志需单独配置:每个子进程初始化自己的
logging.getLogger(),避免写冲突
何时该换用 concurrent.futures.ProcessPoolExecutor
如果你需要混用线程与进程(比如 I/O 等待用线程、计算用进程),或者偏好 Future 模式(提交即返回 handle),ProcessPoolExecutor 更灵活。
立即学习“Python免费学习笔记(深入)”;
- 语法更接近线程池,
submit()返回Future对象,支持as_completed()按完成顺序取结果 - 无法像
Pool那样直接用map(),但可用list(executor.map(...))替代 - 注意:
max_workers设太高会导致系统资源争抢,3.11 中建议不超过os.cpu_count() or 1
实际用哪个取决于你是否需要细粒度控制执行流——简单批处理用 Pool,复杂依赖或混合任务用 ProcessPoolExecutor。
别忘了检查子进程是否真的在跑:用 ps aux | grep python 或任务管理器看进程数,有时候看似并行实则因锁或序列化失败退化成串行。


















