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

Flask多线程HTTP请求场景下内存泄漏问题排查求助

Flask多线程数据收集内存泄漏排查与修复方案

我之前在做批量HTTP请求的多线程任务时也碰到过类似的内存泄漏问题,结合你的代码和描述,咱们一步步来拆解可能的原因和解决办法:

一、线程池残留线程的资源持有问题

ThreadPoolExecutor默认会缓存线程(即使任务完成,线程也不会立即销毁),这些存活的线程可能会持有一些未释放的资源,比如:

  • Flask的应用/请求上下文:如果你的process_valuechains或内部的request_data函数间接用到了Flask的current_app、g等线程本地对象,线程池保留的线程会一直持有这些上下文引用,导致GC无法回收相关内存。
  • 网络连接资源:如果请求函数没有正确关闭连接,线程池的线程会积累未释放的连接句柄或会话对象。

修复建议:

  1. 手动清理Flask上下文:如果子线程中用到了Flask上下文,一定要在任务结束时主动弹出:

    from flask import current_app
    
    def process_valuechains(subprocess, publishedRevision):
        ctx = None
        # 仅当需要使用Flask资源时才推入上下文
        if current_app:
            ctx = current_app.app_context()
            ctx.push()
        try:
            data = []
            new_data_1 = request_data_1(subprocess)
            data.extend(new_data_1)
            new_data_2 = request_data_2(subprocess)
            data.extend(new_data_2)
            return data
        finally:
            if ctx:
                ctx.pop()  # 强制释放上下文资源
    
  2. 强制线程池关闭并清理:虽然with语句会自动调用shutdown(),但显式等待线程销毁并删除池对象,能减少残留:

    pool = ThreadPoolExecutor(max_workers=10)  # 建议根据机器配置设置合理的线程数,不要过大
    data = []
    try:
        for res in pool.map(process_valuechains, subprocesses, repeat(publishedRevision)):
            data.extend(res)
    finally:
        pool.shutdown(wait=True)  # 等待所有线程执行完毕并销毁
        del pool  # 删除池对象,帮助GC回收
        gc.collect()
    

二、HTTP请求函数的资源泄漏

你的request_data_1和request_data_2是内存泄漏的高危点,如果没有正确管理网络连接,会积累大量未释放的资源:

  • 如果用requests库,每次请求都创建新会话而不关闭,会导致连接池资源无法回收;
  • 响应对象没有手动关闭,或者没有用with语句管理。

修复建议:

改用requests.Session()来复用连接,并确保会话被正确关闭:

import requests

def request_data_1(subprocess):
    with requests.Session() as session:
        # 这里替换成你的请求逻辑
        response = session.get(f"your-api-url/{subprocess}")
        response.raise_for_status()  # 处理请求错误
        return response.json()

with语句会自动关闭会话,释放连接池资源,避免线程中积累无效连接。

三、用内存分析工具定位泄漏对象

如果以上方法还没解决问题,建议用tracemalloc工具找到具体泄漏的对象类型,精准排查:

import tracemalloc

# 开启内存跟踪
tracemalloc.start()

# 运行你的多线程数据收集代码
subprocesses = [sub for sub in process]
data = []
with ThreadPoolExecutor(max_workers=10) as pool:
    for res in pool.map(process_valuechains, subprocesses, repeat(publishedRevision)):
        data.extend(res)

# 生成内存快照并打印TOP10泄漏点
snapshot = tracemalloc.take_snapshot()
top_stats = snapshot.statistics('lineno')

print("\n=== 内存泄漏TOP10位置 ===")
for stat in top_stats[:10]:
    print(stat)

通过快照你可以看到哪些代码行创建了最多的未回收对象,比如如果是request_data里的响应对象,或者Flask上下文相关的对象,就能针对性修复。

四、额外注意点

  • 不要在多线程任务中持有全局大对象的引用,比如把publishedRevision这样的对象传入线程时,如果它本身很大,要确保任务完成后没有残留引用;
  • Flask开发服务器的多线程模式和你应用内部的线程池不要混淆,如果是生产环境,建议用Gunicorn等服务器,并合理配置worker数,避免多层多线程叠加导致资源耗尽。

内容的提问来源于stack exchange,提问作者Pter Steiner

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.04.27 20:32:49