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

使用apply_async与tqdm时出现多余空进度条问题排查

多进程Pool结合tqdm出现多行空进度条问题排查

我写了一段代码,用两个独立的multiprocessing Pool处理多份表格,想用tqdm进度条展示处理进度。现在进度展示功能能跑,但出现了多行进度为0的空进度条,找不到问题根源,求帮忙定位哪里错了?


代码

from multiprocessing import Pool, Manager
from tqdm import *
from collections import deque

pbar = tqdm(position=0)


def task(tables_started, right_list, left_list, current_table, process):
    if current_table not in tables_started:
        tables_started.append(current_table)
        if process == "left_process":
            left_list.append(current_table)
        else:
            right_list.append(current_table)
        # print(f"Starting {current_table} running on {process}.")
        return True
    else:
        return False


def return_callback(result):
    # global pbar
    # print(result)
    if result:
        # print("updating")
        pbar.update()
    else:
        # print("not updating")
        pass


if __name__ == '__main__':
    right_tables = deque()
    manager = Manager()
    for i in range(10):
        right_tables.append("Table" + str(i))

    left_tables = right_tables.copy()
    left_tables.reverse()
    # print(left_tables)
    # print(right_tables)

    tables_started = manager.list()
    right_list = manager.list()
    left_list = manager.list()

    left_pool = Pool(processes=2)
    right_pool = Pool(processes=2)
    print(f"left_tables: {left_tables}")
    print(f"right_tables: {right_tables}")
    pbar.total = len(left_tables)
    while left_tables:
        left_table = left_tables.pop()
        left_res = left_pool.apply_async(task, (tables_started, right_list, left_list, left_table, "left_process"), callback=return_callback)
        right_table = right_tables.pop()
        right_res = right_pool.apply_async(task, (tables_started, right_list, left_list, right_table, "right_process"), callback=return_callback)
    
    left_pool.close()
    right_pool.close()

    left_pool.join()
    right_pool.join()

    # print(f"right_list: {right_list}")
    # print(f"left_list: {left_list}")

终端现象
终端中出现多条显示0%的空进度条,仅一条进度条正常更新处理进度。


问题根源

  1. 全局初始化进度条导致子进程复制实例:你在全局作用域创建了tqdm的pbar对象,而multiprocessing Pool启动子进程时会fork父进程的内存空间,每个子进程都会复制这个pbar实例。这些子进程的pbar没有被正确更新,就会在终端输出空的0%进度条。
  2. 任务重复处理导致无效回调:left_tables是right_tables反转后的副本,两者包含完全相同的表格数据,因此一半的任务会因表格已被处理返回False,不会触发进度条更新,进一步凸显了子进程空进度条的问题。

修复方案

  1. 将进度条初始化移到主进程块内:避免子进程复制多余的进度条实例。
  2. 让回调函数正确绑定主进程的进度条:通过闭包或参数传递确保回调只更新主进程的那一条进度条。
  3. 可选:优化任务逻辑避免重复处理:如果你的需求是处理不同表格,需调整left_tables和right_tables的生成逻辑。

修改后的代码:

from multiprocessing import Pool, Manager
from tqdm import tqdm
from collections import deque

def task(tables_started, right_list, left_list, current_table, process):
    if current_table not in tables_started:
        tables_started.append(current_table)
        if process == "left_process":
            left_list.append(current_table)
        else:
            right_list.append(current_table)
        return True
    else:
        return False

def return_callback(result, pbar):
    if result:
        pbar.update()

if __name__ == '__main__':
    right_tables = deque()
    manager = Manager()
    for i in range(10):
        right_tables.append("Table" + str(i))

    left_tables = right_tables.copy()
    left_tables.reverse()

    tables_started = manager.list()
    right_list = manager.list()
    left_list = manager.list()

    left_pool = Pool(processes=2)
    right_pool = Pool(processes=2)
    print(f"left_tables: {left_tables}")
    print(f"right_tables: {right_tables}")
    
    # 主进程内初始化进度条,设置总任务数
    total_tasks = len(left_tables)
    pbar = tqdm(total=total_tasks, position=0)
    
    while left_tables:
        left_table = left_tables.pop()
        # 通过lambda将主进程的pbar传给回调
        left_res = left_pool.apply_async(
            task, 
            (tables_started, right_list, left_list, left_table, "left_process"), 
            callback=lambda res: return_callback(res, pbar)
        )
        right_table = right_tables.pop()
        right_res = right_pool.apply_async(
            task, 
            (tables_started, right_list, left_list, right_table, "right_process"), 
            callback=lambda res: return_callback(res, pbar)
        )
    
    left_pool.close()
    right_pool.close()

    left_pool.join()
    right_pool.join()
    
    # 关闭进度条,清理终端输出
    pbar.close()

额外说明

  • 主进程内初始化的pbar不会被子进程复制,所有回调操作的都是同一个进度条实例。
  • 用lambda传递pbar可以确保回调函数能正确引用主进程的进度条。
  • 调用pbar.close()能确保任务结束后终端输出干净,避免残留进度条状态。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.04 02:55:16