Python multiprocessing.Pool 默认采用“简单轮询分发 + 进程空闲时主动领取”策略,任务由feeder线程推入共享队列,worker阻塞取用,不保证FIFO、无优先级、不感知系统负载。

进程池默认用什么调度策略?
Python 的 multiprocessing.Pool 默认使用「任务窃取(work-stealing)」的变体,但实际行为更接近「简单轮询分发 + 进程空闲时主动领取」。它不保证 FIFO,也不按 CPU 负载动态调整——任务提交后,由内部一个专用的 feeder 线程把任务推入共享队列,worker 进程从该队列阻塞式取任务。
- 任务进入顺序 ≠ 执行顺序,尤其当 worker 执行时间差异大时,后提交的短任务可能先完成
- 没有优先级支持,所有任务在队列里一视同仁
-
Pool不感知系统负载,不会因某个 core 高负载而绕过对应 worker
如何让长任务不阻塞短任务?
核心是避免单个任务耗尽 worker 生命周期。常见做法不是改调度策略(multiprocessing 不暴露调度器接口),而是拆解任务粒度或用替代结构:
- 把「一个长计算」拆成多个
apply_async提交的小任务,例如分块处理数组、分页调 API - 改用
concurrent.futures.ProcessPoolExecutor,配合submit()+as_completed()实现结果驱动的调度感知(谁先返回谁先被处理) - 对极不均衡任务,考虑混合策略:短任务走
Pool,长任务单独起守护进程或用joblib.Parallel(支持更细粒度的 batch 控制)
示例:
from concurrent.futures import ProcessPoolExecutor, as_completeddef heavy_task(x): return x ** 2
立即学习“Python免费学习笔记(深入)”;
with ProcessPoolExecutor(max_workers=4) as exe: futures = [exe.submit(heavy_task, i) for i in range(100)] for f in as_completed(futures): # 按完成顺序返回,非提交顺序 print(f.result())
为什么 map() 比 apply_async() 更容易卡住?
map() 是同步批量提交 + 同步等待全部完成,底层会预先把所有参数序列切片分发给 worker,一旦某个 worker 卡在长任务里,map() 就得等它;而 apply_async() 是异步提交,可随时插入新任务,且能配合回调或超时控制。
-
map()的 chunksize 参数影响显著:设太小 → 频繁加锁争抢队列;设太大 → 某个 worker 被长任务独占,其余空转 -
map_async()只是把等待异步化,任务分发逻辑和map()完全一致,不能解决负载不均 - 若必须用
map类接口,建议显式估算 chunksize:chunksize = max(1, len(data) // (4 * processes))
自定义调度器真的可行吗?
标准 multiprocessing.Pool 不提供替换调度器的钩子。强行实现需绕过 Pool,直接管理 Process + Queue + 自定义分发逻辑,但代价很高:
- 失去
Pool的自动进程生命周期管理、异常传播、join/terminate 语义 - 需手动处理 worker 崩溃、队列阻塞、结果收集乱序等问题
- 实际项目中,95% 的调度痛点靠调整任务粒度、换 executor 或加中间层(如 Redis 队列 + Celery)更可靠
真正需要精细调度的场景,通常已超出 multiprocessing 的设计边界——这时候该考虑专业任务队列,而不是给 Pool 打补丁。









