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

Python多线程执行HTTP请求脚本出现内存泄漏,求助修复方案

内存泄漏问题根因
  • 多线程共享非线程安全的数据库连接:Python常用的MySQL驱动(pymysql、mysql-connector等)都不支持多线程共用同一个连接实例,跨线程操作会导致连接内部缓冲区的资源无法释放,是内存溢出的核心诱因
  • 线程状态管理逻辑存在严重漏洞:你仅将自增的row_n存入threads列表,一旦线程执行过程中出现任何异常,会直接跳过threads.remove(row_n)逻辑,导致threads列表元素只增不减,不仅会让最终的等待循环永远无法退出,列表本身也会占用越来越多的内存
  • 空异常捕获吞掉所有报错:你用裸except捕获所有异常,完全感知不到线程执行过程中的资源释放失败、数据库操作报错等问题,进一步加剧了资源泄漏
  • 大临时对象未及时回收:pair_data、json解析结果等大对象没有清理引用,GC无法及时回收无用内存
修复后的代码

我们用标准库ThreadPoolExecutor替代手动管理线程,同时每个线程创建独立的数据库连接,彻底解决共享资源的问题:

import json
import time
import pymysql # 替换为你实际使用的MySQL驱动
from concurrent.futures import ThreadPoolExecutor

# 数据库配置单独抽离,每个线程自己创建连接
DB_CONFIG = {
    "host": "你的数据库地址",
    "user": "用户名",
    "password": "密码",
    "database": "库名",
    "charset": "utf8mb4"
}

MAX_THREADS = 3

def process_single_row(row):
    tokenid = str(row[0])
    contract = row[1]
    network = row[2]
    # 每个线程单独创建数据库连接,用完自动关闭
    cnx = pymysql.connect(**DB_CONFIG)
    cur2 = cnx.cursor()
    cur3 = cnx.cursor()
    try:
        pair_data = getPairs(network, contract)
        # 插入pairs_history
        cur2.execute("insert into pairs_history (tokenid, data, date) values (%s, %s, %s)",
                    (tokenid, pair_data, time.time()))
        # 解析pair_data并插入prices_history
        pair_list = json.loads(pair_data)
        for x in pair_list:
            try:
                price = x[0][4]
                if price not in ("0.00", "0.0", 0.0):
                    cur3.execute("insert into prices_history (tokenid,price,date,exchange) values (%s,%s,%s,%s)",
                                (tokenid, price, time.time(), x[0][5]))
            except Exception as e:
                # 只捕获单条数据插入的异常,避免整体失败
                print(f"单条价格数据插入失败: {e}")
                continue
        # 统一提交,减少commit次数提升性能
        cnx.commit()
    except Exception as e:
        print(f"处理token {tokenid} 失败: {e}")
        cnx.rollback()
    finally:
        # 无论是否成功,都关闭游标和连接,释放资源
        cur2.close()
        cur3.close()
        cnx.close()
    # 手动清理大对象引用,帮助GC回收
    del pair_data, pair_list, row

if __name__ == "__main__":
    # 主线程单独查所有token数据,避免游标共享
    cnx_main = pymysql.connect(**DB_CONFIG)
    cur_main = cnx_main.cursor()
    cur_main.execute('SELECT * FROM tokens')
    all_rows = cur_main.fetchall()
    cur_main.close()
    cnx_main.close()

    # 用线程池管理并发,自动控制最大线程数
    with ThreadPoolExecutor(max_workers=MAX_THREADS) as executor:
        executor.map(process_single_row, all_rows)
关键优化点说明
  • 每个线程独享数据库连接,彻底解决多线程共享连接导致的资源泄漏问题
  • 线程池自动管理线程生命周期,无需手动维护threads列表,避免线程状态管理的bug
  • 新增finally块保证连接、游标一定会被关闭,即使执行过程中出现异常也不会泄漏资源
  • 优化了异常捕获逻辑,不会吞掉关键报错,方便排查问题
  • 统一提交数据库操作,减少IO次数的同时避免频繁commit导致的连接资源占用
  • 手动清理大对象引用,加速GC回收无用内存

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.10.05 13:12:02