Python 多进程与进程池

多进程绕过 GIL 做 CPU 并行;注意序列化、内存与启动方式,优先进程池管理生命周期。

#type / concept #status / growing #tech / dev #resource / python #tech / lang / python

[!info] 关联笔记

Python 多进程与进程池

这个概念为什么出现

纯 Python CPU 密集(压缩、哈希爆破、图像像素变换)在多线程下难吃满多核。多进程每个进程独立解释器与 GIL,可真正并行,但要付序列化传参与内存成本。

[!abstract] 一句话理解 进程池把 CPU 任务分到多进程执行以绕过 GIL;参数/返回值需可 pickle,适合可分割的计算型负载。

最小可运行示例

先把示例放进业务场景,再看代码:

场景:批量为文件块计算校验和(CPU 模拟)

# process_pool_hash.py
# 业务意图:并行计算多块数据的哈希摘要(用计数模拟 CPU 工作)。
# 教学点:
# - ProcessPoolExecutor;
# - 顶层可 pickle 函数;
# - if __name__ 保护(Windows/spawn)。

from concurrent.futures import ProcessPoolExecutor
from time import perf_counter


def crunch(n: int) -> int:
    # 模拟 CPU:累加
    total = 0
    for i in range(n):
        total += i % 7
    return total


def main() -> None:
    work = [300000] * 4
    t0 = perf_counter()
    with ProcessPoolExecutor(max_workers=4) as pool:
        results = list(pool.map(crunch, work))
    print("results:", results)
    print("elapsed≈", round(perf_counter() - t0, 3))


if __name__ == "__main__":
    main()

建议运行:

python process_pool_hash.py

期望输出(elapsed 随机器变化):

results: [899994, 899994, 899994, 899994]
elapsed≈ 0.xxx

结合场景再看三个关注点

  1. 函数必须可被子进程导入——定义在顶层。
  2. if __name__ == "__main__" 防止 spawn 递归。
  3. 任务太小会被进程开销吃掉收益。

核心概念与准确模型

  • multiprocessing / ProcessPoolExecutor
  • 启动方式:fork/spawn/forkserver
  • 队列、共享内存进阶 API
  • 与线程池接口相似(futures)

边界情况与反直觉行为

  1. lambda/闭包难 pickle。
  2. 大对象传输成为瓶颈。
  3. 全局状态不共享(除显式 IPC)。

常见误区

[!warning] 常见误区:所有变慢都上多进程 错误理解:进程越多越快。
正确模型:先确认 CPU 瓶颈与任务粒度。

工程实践

  • 池大小≈CPU 核数(容器限额内)
  • 批处理合并小任务
  • 失败重试与超时

本节总结

多进程是 CPython CPU 并行主路径之一;把它当分布式计算的缩小版管理。

自测题

  1. 为什么需要 if __name__ 保护?
  2. 为何小任务可能不加速?
参考答案
  1. spawn 会再导入主模块,无保护会重复启动。
  2. 进程启动与序列化开销大于计算本身。

延伸阅读与资料来源

资料类型支撑内容
multiprocessing文档进程
ProcessPoolExecutor文档进程池
创建于 2026/7/15 更新于 2026/7/15