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

Python ThreadPoolExecutor内存泄漏问题排查与多线程内存优化咨询

代码存在的问题
  • 核心错误是遍历future_list时直接删除元素:Python的列表迭代过程中调用remove()会导致迭代器索引错位,大量已完成的future会被跳过而始终保留在列表中,相关的任务上下文、返回值等资源永远无法被回收,这是内存持续上涨的根本原因。
  • 冗余的future存储逻辑:你已经为每个future绑定了done_callback,不需要额外维护全局的future_list来存储所有任务,多余的引用反而会阻碍垃圾回收。
  • 不安全的私有属性操作:executor._threads.clear()、futures.thread._threads_queues.clear()属于直接操作模块私有属性,既没有兼容性保证,也可能引发线程安全问题,ThreadPoolExecutor的上下文管理器会自动处理异常退出时的资源回收,不需要手动干预。
  • 手动del变量无意义:Python靠引用计数管理内存,只要对象没有被活跃引用就会被自动回收,你在回调函数末尾手动del变量的操作完全多余,只要future本身还被future_list引用,内部的所有关联资源都不会释放。
优化后的实现方案

你可以直接移除future_list相关逻辑,靠回调自动处理任务结果即可,线程池本身会复用固定数量的工作线程,不会无限制创建资源:

from concurrent import futures
from random import seed, randint
import time
import psutil
import os
import threading

PROCESS = psutil.Process(os.getpid())

def get_mem_usage():
    return PROCESS.memory_info().rss // 1024

def check_for_even(my_number: int) -> int | None:
    thread_name = threading.current_thread().name
    print(f"{thread_name} - Checking for even: {my_number}")
    return my_number if is_even(my_number) else None

def get_number() -> int:
    return randint(1, 10)

def is_even(random_number: int) -> bool:
    return random_number % 2 == 0

def got_number(future: futures.Future):
    thread_name = threading.current_thread().name
    # 这里的大列表x会在回调执行完后没有引用,自动被回收
    x = [n for n in range(int(1e6))]
    result = future.result()
    if result:
        print(f"{thread_name} - Got even number: {result}")
    else:
        print(f"{thread_name} - Number was not even")

def main():
    seed(int(time.time()))
    MAX_WORKERS = 5
    with futures.ThreadPoolExecutor(max_workers=MAX_WORKERS) as executor:
        while True:
            try:
                print(f"Memory: {get_mem_usage()} KB")
                print(f"Active thread count: {threading.active_count()}")
                number = get_number()
                future = executor.submit(check_for_even, number)
                future.add_done_callback(got_number)
                # 简单限流,避免任务提交速度远大于处理速度导致堆积
                while executor._work_queue.qsize() > MAX_WORKERS * 2:
                    time.sleep(0.001)
            except KeyboardInterrupt:
                # 上下文管理器会自动关闭线程池,清理资源
                raise

if __name__ == '__main__':
    main()
补充说明

如果优化后依然看到RSS缓慢上涨,大概率是Python内存分配器的特性:Python回收的小块内存会保留在进程内的内存池复用,不会立刻归还给操作系统,这种情况内存不会无限上涨,会稳定在一个合理的阈值,不属于泄漏。如果内存还是无限增长,再排查是否有其他全局对象持有任务的引用。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.09.30 14:06:02