ProcessPoolExecutor 报 BrokenProcessPool,先找 Exception in initializer
BrokenProcessPool 前有 initializer 异常?先检查 worker 实际模型路径与加载结果,补齐资源后关闭失效池,并按完成状态重建任务。
主进程已经取得输入数据,提交处理任务时却报 BrokenProcessPool,日志前面还有 Exception in initializer。先保留 worker 初始化的完整 traceback:池失效是后续结果,真正失败的资源加载通常在前面。
例如 initializer 调用 dlib.shape_predictor(),报 Unable to open shape_predictor_68_face_landmarks.dat。这条路径需要检查 worker 实际可见的模型文件,不应因为网络请求已经成功,就继续排查远端 API。
initializer 在任务函数执行前失败
ProcessPoolExecutor 的 initializer 用来在各 worker 开始工作时执行初始化,initargs 提供参数。该能力自 Python 3.7 提供;初始化函数抛出异常,会使池无法继续正常处理,等待中的任务和后续提交都可能报 BrokenProcessPool。
保存最早的初始化错误、实际解释器、启动参数和文件路径。若没有 Exception in initializer,还需考虑进程异常退出、原生库崩溃或内存限制等证据;不是每条 BrokenProcessPool 都表示缺模型文件。
任务函数无法 pickle 则是另一阶段的问题,可看 ProcessPoolExecutor 的局部函数序列化检查。把函数移到模块顶层,不会补齐 initializer 要读取的外部资源。
检查 worker 的路径,不只检查终端目录
模型路径为相对路径时,核对 worker 的实际工作目录及配置来源。服务、容器和命令行可能使用不同目录;父进程能找到文件,也不能证明每个 worker 的用户、挂载和路径均一致。
检查需要的是解压后的 .dat,还是仍只有 .dat.bz2 压缩包。文件存在还要确认读取权限、大小和实际加载结果,不能把存在性检查当作模型有效性校验。容器镜像内带有资源,也不代表本机直接运行时自动获得同一文件。
自己维护的应用可以在原初始化函数中明确解析模型路径,并保留加载异常。以下仅展示 dlib 资源初始化部分,需合入原函数,保留其他必要初始化:
from pathlib import Path
import dlib
predictor = None
def init_worker(model_path):
global predictor
path = Path(model_path).resolve(strict=True)
if not path.is_file():
raise FileNotFoundError(f"model is not a regular file: {path}")
predictor = dlib.shape_predictor(str(path))
通过原来的 initargs 传入经确认的路径。resolve 和普通文件检查能给出更早的路径诊断,真正的模型格式和内容仍由加载器验证。不要捕获所有异常后继续,让任务在 predictor 未初始化时运行。
补齐文件应使用应用认可的资源分发方式,核对模型版本和使用条件;不把任意网上同名文件当作替代,也不需要为了这个诊断临时放开目录全部权限。
资源修好后,要建立新池
已经失效的 executor 不会因为文件后来出现就恢复。先停止向旧池提交工作,按应用流程处理等待任务和关闭池,再用修正后的初始化条件建立新池。
任务可能已经部分执行过。重建前记录哪些输入已完成、哪些只是失败或结果未收取,按业务幂等规则决定重提范围,不能把整个批次无条件重复执行。涉及外部写入时,还要核对结果状态。
父进程提前检查资源可以减少明显错误,但不能替代 worker 中的真实加载验证。初始化函数及任务入口仍须满足当前进程启动方式的导入要求;不要把改成 fork 或无限重试当作资源缺失的修复。
保存模型配置和原启动参数,在隔离环境用少量输入验证每个 worker 能完成初始化、任务成功返回、原输出确实生成且池正常关闭。若原生加载器仍失败,继续核对格式、版本和文件完整性。回退时同时恢复资源路径与兼容配置,保留已处理输入的状态,避免重复写入。


