multiprocessing.Pool通过预创建固定数量子进程并复用它们来避免频繁创建开销;正确做法是跨批次复用同一池实例,且Windows下必须加if name == "__main__":保护。

直接用 multiprocessing.Pool 就行,它默认复用子进程——只要你不主动 close() 或 terminate(),池里的进程会一直活着等下一个任务。
为什么 Pool 能避免频繁创建开销?
因为 Pool 在初始化时就 fork(Unix/Linux)或 spawn(Windows)出固定数量的子进程,之后所有任务都分发给这些已存在的进程执行。没有“来一个任务启一个进程”的逻辑,也就没有反复调用 os.fork() 或启动解释器的开销。
常见错误是每次处理一批任务就新建一个池:
for batch in batches:
with multiprocessing.Pool(4) as pool: # ❌ 每次都重建池
pool.map(func, batch)
这等于每批任务都重新 fork 4 个进程,销毁旧的,开销反而比单进程大。
立即学习“Python免费学习笔记(深入)”;
正确复用 Pool 的两种写法
核心原则:池对象要跨批次复用,生命周期覆盖全部任务。
安全 Python 代码沙箱:在隔离的沙箱中执行任意 Python 代码,预装 requests、NumPy、pandas 库。返回 stdout、stderr。
- 用
with语句但只包住整个任务流,不是每个批次:with multiprocessing.Pool(4) as pool: for batch in batches: results = pool.map(func, batch) # ✅ 复用同一组子进程 - 手动管理生命周期(适合长期运行服务):
pool = multiprocessing.Pool(4) try: for batch in batches: results = pool.map(func, batch) finally: pool.close() # 不再接收新任务 pool.join() # 等待子进程自然退出
map / apply_async / starmap 的复用效果一样吗?
一样。只要池没关,无论你调用 map、apply_async 还是 starmap,底层都是把任务塞进同一个任务队列,由空闲子进程取走执行。区别只在接口和返回方式:
-
map:阻塞,等全部完成才返回 list -
apply_async:非阻塞,返回AsyncResult,可调.get()等结果 -
starmap:类似map,但参数是元组解包传入,适合多参数函数
它们都不影响子进程复用——复用与否只取决于池是否存活。
容易被忽略的坑:Windows 下的 spawn + 全局作用域限制
Windows 默认用 spawn 启动子进程,要求主模块必须有 if __name__ == "__main__": 保护,否则子进程重启时会重复执行池创建代码,导致递归创建或 RuntimeError: An attempt has been made to start a new process before the current process has finished its bootstrapping phase.
所以哪怕你只是想测试复用,也得加这个守卫:
if __name__ == "__main__":
with multiprocessing.Pool(4) as pool:
results = pool.map(my_func, data)
漏掉这行,在 Windows 上跑一次就卡死,根本轮不到“复用”这一步。

















