多线程环境下tqdm进度条随机重复问题排查求助
解决多线程下tqdm进度条重复混乱的问题
问题根源
你遇到的进度条混乱,核心是三个原因:
- 所有文件的进度条都用了
position=0,多个线程的进度条抢占同一终端行,导致输出互相覆盖。 - 多线程无锁调用
pbar.update(1),tqdm的更新操作不是线程安全的,竞态条件会引发进度计数错误或输出错乱。 - 两层线程池嵌套(外层处理文件、内层处理子任务)进一步放大了终端输出的冲突。
解决方案
1. 给每个进度条分配唯一的终端位置
用全局计数器给每个文件任务分配独立的position值,确保每个进度条占单独一行,不会互相覆盖。
2. 确保进度条更新线程安全
通过tqdm自带的lock参数,或者手动加互斥锁,保护update操作的原子性,避免竞态条件。
3. 自动清理完成的进度条
在tqdm参数里加leave=False,任务结束后进度条会自动清除,保持终端整洁。
修改后的代码示例
import os from itertools import count import threading from concurrent.futures import ThreadPoolExecutor from tqdm import tqdm # 全局计数器,为每个文件的进度条分配唯一位置 pos_counter = count(0) # 全局锁,保证进度条更新线程安全 update_lock = threading.Lock() def main(): with ThreadPoolExecutor() as executor: # 遍历files目录下的JSON文件 for filename in os.listdir("files"): if filename.endswith(".json"): # 给当前文件任务分配唯一的进度条位置 current_pos = next(pos_counter) executor.submit(handle, filename, current_pos) def handle(filename, current_pos): # 读取源JSON文件(替换成你的读取逻辑) with open(f"files/{filename}", "r", encoding="utf-8") as f: data = f.read() # 调用翻译处理,传入进度条位置 parseMap(data, filename, current_pos) # 写入翻译结果到translated目录(替换成你的写入逻辑) with open(f"translated/{filename}", "w", encoding="utf-8") as f: f.write(data) def parseMap(data, filename, current_pos): # 计算总进度数(替换成你的实际计数逻辑) total_items = len(data) if isinstance(data, list) else 100 # 创建带唯一位置、锁的进度条,任务完成后自动清理 with tqdm( total=total_items, desc=filename, position=current_pos, lock=update_lock, leave=False ) as pbar: # 模拟拆分数据为子任务块(替换成你的实际拆分逻辑) data_chunks = [data[i:i+10] for i in range(0, total_items, 10)] # 内层线程池处理子任务 with ThreadPoolExecutor() as inner_executor: # 给每个子任务传入进度条实例 inner_executor.map(searchCodes, data_chunks, [pbar]*len(data_chunks)) def searchCodes(chunk, pbar): # 遍历子任务数据(替换成你的翻译逻辑) for item in chunk: # 执行翻译操作... # 加锁更新进度条,保证线程安全 with update_lock: pbar.update(1)
关键修改点说明
pos_counter:通过itertools.count生成递增的位置值,每个文件的进度条占用独立终端行。lock=update_lock:tqdm初始化时传入锁,确保进度条的输出和计数操作不会被多线程打断。leave=False:任务完成后自动清除当前进度条行,避免终端残留无效内容。- 内层子任务更新进度条时手动加锁:进一步保障
update操作的原子性,防止多线程同时修改进度计数。
修复后效果示例
每个文件的进度条会在独立行显示,完成后自动清理,不会出现互相覆盖的情况:
Map023.json: 15%|█▌ | 255/1698 [00:00<00:01, 1280.56it/s]
Map017.json: 70%|███████ | 29/42 [00:00<00:00, 78.34it/s]
files/Map004.json: [0 Tokens/$0.0000][0.3s] ✓
内容的提问来源于stack exchange,提问作者DazedFury
相关产品推荐
相关产品推荐

