Windows下Flask用Python multiprocessing下载图片内存过高如何解决
问题根因
Windows平台下Python的multiprocessing默认使用spawn方式启动子进程,而非类Unix系统的fork。spawn启动子进程时会重新导入整个主入口模块,模块顶层所有代码(包括所有import语句)都会执行,不受if __name__ == xxx守卫块限制——守卫块仅能避免块内代码执行,无法阻止顶层导入的运行,因此子进程会加载主Flask应用的所有依赖,导致内存虚高。
可行解决方案
- 优先改用多线程实现下载逻辑:你的场景属于典型IO密集型任务,网络等待时GIL会自动释放,多线程性能完全不输多进程,且无进程内存复制开销。直接替换为
concurrent.futures.ThreadPoolExecutor即可,示例代码如下:
from concurrent.futures import ThreadPoolExecutor # 线程数可根据实际下载速率调整,一般设为16~32即可满足32~64张图片的下载需求 with ThreadPoolExecutor(max_workers=16) as executor: img_dicts = list(executor.map(download, url_dicts))
- 必须使用多进程时,优化导入逻辑与代码结构:
- 将主入口模块(application.py)顶层的非公共依赖(如Flask扩展、torch、数据处理类库等)全部移入
if __name__ == xxx守卫块内部,仅保留子进程运行需要的最小导入在顶层。 - 把下载逻辑完全抽离到独立文件(如
download_utils.py),该文件仅导入下载需要的依赖(如io、requests、PIL等),完全不引入Flask相关代码。主应用调用多进程时,仅导入该独立文件的download函数即可。 - 显式指定进程启动方式为
spawn,并缩小进程池规模:
- 将主入口模块(application.py)顶层的非公共依赖(如Flask扩展、torch、数据处理类库等)全部移入
import multiprocessing if __name__ == "__main__": multiprocessing.set_start_method("spawn", force=True) # 进程数不需要设为核心数的一半,IO密集型场景下2~4个进程足够,避免过多进程冗余加载 pool = multiprocessing.Pool(4, maxtasksperchild=2) img_dicts = pool.map(download, url_dicts) pool.close() pool.join()
- 替换轻量的图片读取依赖:
scikit-image的io.imread依赖加载开销较高,可改用requests下载二进制内容+PIL.Image读取的组合,减少子进程依赖加载的内存占用:
import requests from PIL import Image import io def download(url_dict): try: resp = requests.get(url_dict["img_url"], timeout=10) resp.raise_for_status() image = Image.open(io.BytesIO(resp.content)) except Exception: image = None return {"post_id": url_dict["post_id"], "img": image}
内容的提问来源于stack exchange,提问作者Michael S
相关产品推荐
相关产品推荐

