使用python多进程处理数据时,往往需要显示处理进度。大家首选的第一赶脚是应用tqdm模块显示数据进度,But,数据显示竟然是重叠的…
其实,该模块是支持多行显示的,进过测试,小有发现。但事先声明一下,以下程序在pycharm中显示会乱行,在命令行窗口显示是正常的,原因嘛,,,我也不清楚。
import time
from multiprocessing import Pool, RLock, freeze_support
from tqdm import tqdm
def my_process(process_name):
# tqdm中的position参数需要设定呦!!!
pro_bar = tqdm(range(50), ncols=80, desc=f"Process—{process_name} pid:{str(os.getpid())}",
delay=0.01, position=process_name, ascii=False)
for file in pro_bar :
time.sleep(0.2)
pro_bar.close()
if __name__ == '__main__':
print(f'父进程 {os.getpid()}')
freeze_support()
pro_num = 3
# 多行显示,需要设定tqdm中全局lock
p = Pool(pro_num , initializer=tqdm.set_lock, initargs=(RLock(),))
for idx in range(pro_num ):
p.apply_async(image_process, kwds={"process_name": idx})
p.close()
p.join()
运行效果如下图所示:(注意呦,展示的是在pycharm中的terminal中进行的)
另外,多进程显示处理进度方法也可以是单独开一个进程显示进度,其他进程仅进行数据处理。具体实现以后补充…
另一种解决思路就是:数据预处理和进度显示为单独进程,数据处理为其他进程,它们之间通过队列进行信息和数据的交互。示例代码如下:
from multiprocessing import Process, Queue, Manager
from tqdm import tqdm
from random import random, choice, randint
import string
import time
def load_data(data_queue, status_queue, pro_num):
"""
加载数据
:param status_queue: 数据处理进程准备状态
:param data_queue: 加载数据队列
:param pro_num: 推理进程数量
:return: None
"""
# 等待推理器初始化完成
print('推理器初始化中...')
predictor_num = 0
start_t = time.time()
while predictor_num != pro_num:
if not status_queue.empty():
status_msg = status_queue.get()
predictor_num += 1
print(status_msg)
else:
time.sleep(0.05)
end_t = time.time()
print(f'推理器初始化完成,共耗时{(end_t - start_t):.2f}s')
# 加载图像数据到image_queue中
image_files = range(100)
image_progress = tqdm(image_files)
for _ in image_progress:
# 等待图像队列数据被处理
while data_queue.full():
time.sleep(0.05)
# 图像加载到队列中
cur_time = time.strftime('%Y-%m-%d %H:%M:%S', time.localtime(time.time()))
image_progress.set_description(f'{cur_time}: [PID {os.getpid()}]-load_data')
time.sleep(0.1)
data_queue.put({"file_name": f"{''.join([choice(string.ascii_letters) for _ in range(6)])}.bmp"})
# 输入加载完成信号
while not data_queue.empty():
time.sleep(0.05)
[data_queue.put('OK') for _ in range(pro_num)]
def process_data(gpu_id, data_queue, status_queue, share_results):
"""
推理图像
:param gpu_id: 推理显卡ID
:param data_queue: 推理数据队列,推理数据来源
:param status_queue: 推理器状态队列
:param share_results: 处理结果
:return: None
"""
# 实例化推理器
time.sleep(randint(1, 5))
cur_time = time.strftime('%Y-%m-%d %H:%M:%S', time.localtime(time.time()))
status_queue.put(f'{cur_time}: 推理器[PID-{os.getpid()}, GPU-{gpu_id}]初始化完成!')
# 初始化变量
result_buffer = {}
# 循环推理
while True:
# 等待图像加载到队列中
while data_queue.empty():
time.sleep(0.05)
# 从队里中取数据进行处理
data = data_queue.get()
if data == 'OK':
# 最后将结果再写入共享变量中,减少中间访问时,变量切换造成的资源浪费
if len(result_buffer):
share_results.update(result_buffer)
result_buffer.clear()
break
else:
fn = data['file_name']
# 图像推理
time.sleep(0.5)
# 保存推理详细信息
result_buffer[fn] = {"scores": random()}
def multiprocess_data(gpu_ids, predictor_per_gpu):
"""
多进程推理图像,适用于加速推理大量图像
:param gpu_ids: 推理显卡ID
:param predictor_per_gpu: 每个显卡上实例化推理器的个数
:return: None
"""
# 初始化变量
gpu_ids = [gpu_id for gpu_id in gpu_ids for _ in range(predictor_per_gpu)]
gpu_pro_num = len(gpu_ids)
image_q = Queue(maxsize=gpu_pro_num * (gpu_pro_num + 2))
status_q = Queue()
inf_results = Manager().dict()
# 定义推理进程并依次启动
predict_processes = [Process(target=process_data, args=(id, image_q, status_q, inf_results)) for id in gpu_ids]
predict_processes.insert(0, Process(target=load_data, args=(image_q, status_q, gpu_pro_num)))
[predict_process.start() for predict_process in predict_processes]
[predict_process.join() for predict_process in predict_processes]
# 保存最终嘴里结果
for key, val in inf_results.items():
pass
print('Inference is finished!')
if __name__ == '__main__':
multiprocess_data([0, 1], 3)
若还有其他处理方法,欢迎留言~ 觉得写的不错的,可以收藏+关注哦