You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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))
  • 必须使用多进程时,优化导入逻辑与代码结构:
    1. 将主入口模块(application.py)顶层的非公共依赖(如Flask扩展、torch、数据处理类库等)全部移入if __name__ == xxx守卫块内部,仅保留子进程运行需要的最小导入在顶层。
    2. 把下载逻辑完全抽离到独立文件(如download_utils.py),该文件仅导入下载需要的依赖(如io、requests、PIL等),完全不引入Flask相关代码。主应用调用多进程时,仅导入该独立文件的download函数即可。
    3. 显式指定进程启动方式为spawn,并缩小进程池规模:
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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.09.30 11:48:03