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
相关产品推荐
相关产品推荐

