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

多线程环境下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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.25 12:55:15