Flask多图处理使用multiprocessing时如何避免MemoryError?
解决方案说明
你的内存问题判断是准确的,同时multiprocessing.Pool完全可以实现你需要的固定进程上限调度,是当前问题的最优适配实现路径。
原实现的问题根源
你之前使用原生multiprocessing.Process的写法没有任何并发节流逻辑:
- 循环中直接调用
process.start()的操作没有触发条件限制,系统会尽可能快地为所有待启动进程分配基础运行内存、加载进程上下文 - 当进程数超过25个时,单进程的基础内存开销叠加OpenCV读图、图像掩码计算的业务内存占用,会快速耗尽服务器RAM,触发
MemoryError - 这种无限制并发的逻辑,和你需要的“达到进程上限就暂停新任务调度、等旧进程退出再补位新任务”的目标完全不匹配。
Pool的适配实现
multiprocessing.Pool的原生机制就匹配你的调度需求:
- 初始化时根据设定的
processes参数创建固定数量的常驻工作进程,这部分进程只会在初始化时占用一次基础内存,不会随任务数增长额外分配内存 - 所有待处理任务会进入容量可控的任务队列,仅当池内有工作进程处于空闲状态时,才会将队列中的下一个任务递交给空闲进程执行,从机制上杜绝了一次性创建大量进程导致的内存爆涨问题
- 任务执行完成后工作进程不会销毁,会自动承接队列中的下一个任务,省去了频繁创建、销毁进程的额外系统开销。
对应的改写代码示例如下:
# 根据服务器实际可用内存计算最大并发数,建议按:单业务进程峰值内存 × 进程数 < 可用RAM的70% 估算,预留Flask服务和系统本身的内存开销 MAX_WORKERS = 16 all_files = glob.glob(os.path.join(files_path, '*')) # 构造每个任务的入参,和原create_single_model的参数顺序保持一致 task_params = [ (filename, models_path, num_model_photos, uid, idx) for idx, filename in enumerate(all_files) ] with multiprocessing.Pool(processes=MAX_WORKERS) as pool: # starmap会自动按照固定并发上限调度所有任务,不会一次性拉起所有处理进程 pool.starmap(create_single_model, task_params)
落地注意事项
- 如果需要实时更新处理进度,不要使用阻塞式的
starmap,可以改用imap_unordered逐批接收任务完成结果,顺便实现你代码里的进度百分比计算逻辑:with multiprocessing.Pool(processes=MAX_WORKERS) as pool: total_task = len(task_params) for finish_count, _ in enumerate(pool.imap_unordered( lambda params: create_single_model(*params), task_params )): current_percentage = round((finish_count + 1)/total_task * 100) - 不建议手动实现原生Process的节流逻辑:如果坚持用原生
Process实现,需要手动维护运行进程计数器、通过join()等待进程回收再启动新任务,这套自定义逻辑的可靠性远低于官方维护的multiprocessing.Pool,很容易出现僵尸进程、内存泄漏的问题。
内容的提问来源于stack exchange,提问作者francesco
相关产品推荐
相关产品推荐

