如何在multiprocessing函数内用tqdm追踪for循环进度?
解决Jupyter Notebook多进程+外部exe的实时多进度条监控问题
针对你在Jupyter中用multiprocessing启动多个外部exe实例,需要实时监控每个实例迭代进度的需求,以下是可行的解决方案:
核心思路
利用**进程间队列(Queue)**传递进度信息:子进程仅负责收集进度数据并发送到队列,主进程启动专门线程监听队列,更新对应的tqdm进度条。这种方式避免了直接传递tqdm实例导致的序列化错误,同时确保进度条在Jupyter前端正确显示(而非输出到Anaconda Prompt)。
具体实现
1. 修改外部函数文件Functions.py
让函数接收进度队列作为参数,每次迭代完成后发送进度数据:
def run_exe_instance(args, progress_queue): import os import subprocess # 从args中解析所需参数(根据你的实际参数调整) directory, notebookFolder, number_of_exe_iterations, filename = args os.chdir(directory) # 开启text=True,直接处理字符串输出,避免字节流判断问题 exe_process = subprocess.Popen(["program.exe"], shell=True, stdout=subprocess.PIPE, text=True) os.chdir(notebookFolder) for i1 in range(number_of_exe_iterations): line = exe_process.stdout.readline() if line: # 发送进度消息:(任务标识, 当前进度, 总进度) progress_queue.put((filename, i1 + 1, number_of_exe_iterations)) # 确保外部进程完全结束 exe_process.wait() # 发送完成信号 progress_queue.put((filename, number_of_exe_iterations, number_of_exe_iterations)) print(f"{filename} 已完成!")
2. 修改Jupyter主脚本
创建共享队列、初始化进度条、启动监控线程、管理进程池:
import multiprocessing as mp from multiprocessing import Manager from tqdm.notebook import tqdm # 适配Jupyter的tqdm版本 import Functions as f def monitor_progress(progress_queue, progress_bars): """监听队列,更新进度条的线程函数""" while True: msg = progress_queue.get() if msg is None: # 收到结束信号,退出线程 break filename, current, total = msg # 更新对应进度条的当前值并刷新 progress_bars[filename].n = current progress_bars[filename].refresh() # 进度完成时关闭进度条 if current >= total: progress_bars[filename].close() if __name__ == "__main__": __spec__ = None # Jupyter多进程bug修复 # 准备任务参数列表(根据你的实际需求调整) args_list = [ ("path/to/dir1", "/path/to/notebook/folder", 100, "任务A"), ("path/to/dir2", "/path/to/notebook/folder", 150, "任务B") ] with Manager() as manager: # 创建跨进程共享的队列 progress_queue = manager.Queue() # 初始化每个任务的进度条,设置不同position避免重叠 progress_bars = {} bar_pos = 0 for args in args_list: filename = args[3] total_steps = args[2] progress_bars[filename] = tqdm(total=total_steps, desc=filename, position=bar_pos) bar_pos += 1 # 启动进度监控线程(设为守护线程,主进程结束时自动退出) monitor_thread = mp.Process(target=monitor_progress, args=(progress_queue, progress_bars)) monitor_thread.daemon = True monitor_thread.start() # 启动进程池,将队列作为参数传入每个任务 pool = mp.Pool(len(args_list)) # 给每个任务参数追加队列参数,适配starmap的传参格式 pool.starmap(f.run_exe_instance, [(args + (progress_queue,)) for args in args_list]) # 发送结束信号给监控线程 progress_queue.put(None) monitor_thread.join() # 关闭进程池 pool.close() pool.join() # 清理剩余进度条 for bar in progress_bars.values(): bar.close()
关键细节说明
- 进程间通信:用
Manager.Queue而非普通mp.Queue,确保队列能在Pool的子进程间正常共享。 - Jupyter适配:使用
tqdm.notebook.tqdm而非普通tqdm,进度条会在Jupyter前端正确渲染,不会输出到启动终端。 - 进度条定位:通过
position参数设置每个进度条的位置,避免多进度条互相覆盖。 - 序列化问题:子进程仅传递简单的字符串和数字(进度数据),无需传递
tqdm实例,彻底避开pickle序列化错误。
内容的提问来源于stack exchange,提问作者Arthur Booling
相关产品推荐
相关产品推荐

