future.set_result

本文介绍了Tornado框架中Future对象的set_result方法的作用及使用方式。当异步操作完成时,通过调用set_result方法将结果保存在Future中,并触发相应的回调函数执行。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

tornado中future对象的set_result方法负责将异步执行的结果保存在future中:

当某个回调函数执行时,如果该函数内异步操作返回result时,调用future.set_result(result),同时触发该future的_set_done()方法,执行future._callback中的回调函数,该回调函数将callback(run)注册进ioloop中的callbacks队列,在下一次循环时执行。

def callback(result, future):

    if result:

         future.set_result(result)

    else:

         IOLoop.instance().add_callback(callback, result, future)

from concurrent.futures import ThreadPoolExecutor import multiprocessing import multiprocessing.queues import threading import time import asyncio import numpy as np import grpc from grpc import aio import argparse import os import video_service_pb2 import video_service_pb2_grpc class FPSCounter: def __init__(self, window_size=0.5): self.window_size = window_size # in seconds self.frame_timestamps = [] self.last_print_time = time.time() def add_frame(self): now = time.time() self.frame_timestamps.append(now) # Remove frames older than window_size while self.frame_timestamps and now - self.frame_timestamps[0] > self.window_size: self.frame_timestamps.pop(0) def get_fps(self): if not self.frame_timestamps: return 0.0 time_span = self.frame_timestamps[-1] - self.frame_timestamps[0] if time_span <= 0: return len(self.frame_timestamps) return len(self.frame_timestamps) / time_span def should_print(self): now = time.time() if now - self.last_print_time >= self.window_size: self.last_print_time = now return True return False class InferencerProcess(multiprocessing.Process): def __init__(self, inference_queue, result_queue): super().__init__() self.inference_queue = inference_queue # 从推理队列获取数据 self.result_queue = result_queue # 发送结果到结果队列 self.running = True self.lpd = None self.lnr = None self.tensor = None def init_model(self): """初始化推理模型""" import torch pid = os.getpid() print(f"[推理进程-{pid}] 初始化模型...") if torch.cuda.is_available(): torch.cuda.init() torch.cuda.set_device(0) print(f"[推理进程-{pid}] CUDA初始化成功,设备: {torch.cuda.get_device_name(0)}") else: raise RuntimeError("CUDA not available in worker process") from src.alpr.core.model_loader import Predictor, get_model from src.alpr.core.LicensePlateProcessor import project_root lpd_classes = ("LicensePlate",) lnr_classes = ("0","1","2","3","4","5","6","7","8","9", "A","B","C","D","E","F","G","H","J","K", "L","M","N","P","Q","R","S","T","U","V", "W","X","Y","Z") lpd_model = get_model(project_root/"models/yolox_lpd_s_20240201.pth", num_classes=len(lpd_classes)) lnr_model = get_model(project_root/"models/yolox_lnr_s_20240201.pth", num_classes=len(lnr_classes)) self.lpd = Predictor(lpd_model, obj_labels=lpd_classes, confthre=0.01, img_size=(544, 960)) self.lnr = Predictor(lnr_model, lnr_classes, confthre=0.7, img_size=(320, 640)) print(f"[推理进程-{pid}] 模型初始化完成") # self.tensor = torch.as_tensor(np.ones((3, 1080, 1920), dtype=np.uint8), device='cuda') def run(self): pid = os.getpid() print(f"[推理进程-{pid}] 启动成功") try: self.init_model() while self.running: try: # 从推理队列获取数据 data = self.inference_queue.get(block=False) if not data: continue tensor, rtsp_id, request_id = data #tensor = self.tensor # 执行推理 try: out, _, _ = self.lpd.inference(tensor) out, T3, T4 = self.lnr.inference(tensor) #out, T3, T4 = [], 0, 0 # 将检测结果转换为仅包含基本类型的字典 detections = [] for det in out: # 确保所有值都是Python基本类型 detection = { 'label': str(det['label']), 'confidence': float(det['confidence']), 'x1': float(det['bbox'][0]), 'y1': float(det['bbox'][1]), 'x2': float(det['bbox'][2]), 'y2': float(det['bbox'][3]) } detections.append(detection) # 推理成功,将结果放入结果队列 (仅包含可序列化对象) res = { 'status': 'success', 'data': { 'detections': detections, 'rtsp_id': rtsp_id, 'request_id': request_id, 'timing': { 't3': float(T3) if T3 is not None else 0, 't4': float(T4) if T4 is not None else 0 } } } self.result_queue.put_nowait(res) except Exception as e: error_msg = f"推理失败: {str(e)}" print(f"[推理进程-{pid}] {error_msg}") self.result_queue.put_nowait({ 'status': 'error', 'data': { 'message': error_msg, 'rtsp_id': rtsp_id, 'request_id': request_id } }) except multiprocessing.queues.Empty: time.sleep(0.001) except Exception as e: print(f"[推理进程-{pid}] 异常: {str(e)}") except Exception as e: print(f"[推理进程-{pid}] 初始化失败: {str(e)}") print(f"[推理进程-{pid}] 已停止") class DecodeProcess(multiprocessing.Process): def __init__(self, decode_queue, inference_queue, result_queue): super().__init__() self.decode_queue = decode_queue # 接收h264数据 self.inference_queue = inference_queue # 发送解码结果 self.result_queue = result_queue # 发送错误信息 self.running = True self.fps_counter = FPSCounter() from av_decoder import AVDecoder self.decoder = AVDecoder() print(f"[Decode进程-{os.getpid()}] 初始化完成") def run(self): pid = os.getpid() print(f"[Decode进程-{pid}] 启动成功") while self.running: try: # 从队列获取h264数据 data = self.decode_queue.get(block=False) if not data: continue h264_data, rtsp_id, request_id = data tensor, t2, t5 = self.decoder.decode(h264_data, rtsp_id) if tensor is None: error_msg = "Failed to decode H264 to tensor" print(f"[Decode进程-{pid}] {error_msg}, request_id={request_id}") self.result_queue.put_nowait({ 'status': 'error', 'data': { 'message': error_msg, 'rtsp_id': rtsp_id, 'request_id': request_id } }) continue if self.fps_counter.should_print(): print(f"Decoder FPS: {self.fps_counter.get_fps():.2f}") # 解码成功,发送到推理队列 try: self.inference_queue.put_nowait((tensor, rtsp_id, request_id)) self.fps_counter.add_frame() except multiprocessing.queues.Full: self.result_queue.put_nowait({ 'status': 'error', 'data': { 'message': '推理队列已满', 'rtsp_id': rtsp_id, 'request_id': request_id } }) print(f'Inference 隊列已滿') self.fps_counter.add_frame() except multiprocessing.queues.Empty: time.sleep(0.001) # 避免忙等待 except Exception as e: print(f"[Decode进程-{pid}] 异常: {str(e)}") break print(f"[Decode进程-{pid}] 已停止") class FinalizerProcess(multiprocessing.Process): def __init__(self, result_queue, response_queue): super().__init__() self.result_queue = result_queue # 从推理队列获取结果 self.response_queue = response_queue # 将处理好的结果发送回主进程 self.running = True def run(self): pid = os.getpid() print(f"[Finalizer进程-{pid}] 启动成功") while self.running: try: # 从结果队列获取数据 result_dict = self.result_queue.get(block=False) #print("RES:", result_dict) if result_dict: # 将结果发送回主进程 self.response_queue.put(result_dict) except multiprocessing.queues.Empty: continue except Exception as e: print(f"[Finalizer进程-{pid}] 异常: {str(e)}") break print(f"[Finalizer进程-{pid}] 已停止") class ResponseHandler: """在主进程中处理响应的类""" def __init__(self): self.pending_requests = {} # request_id -> (context, response_future) self.lock = threading.Lock() def register_request(self, request_id, context, response_future): """注册新请求""" with self.lock: self.pending_requests[request_id] = (context, response_future) def process_response(self, response_dict): """处理从Finalizer进程返回的响应""" try: data = response_dict['data'] request_id = data['request_id'] with self.lock: if request_id not in self.pending_requests: print(f"请求ID {request_id} 未找到上下文") return context, response_future = self.pending_requests.pop(request_id) # 当前全部返回 Empty Response response_future.set_result(video_service_pb2.DecodeAndInferResponse()) return if response_dict['status'] == 'success': # 构造gRPC响应 detections = [] for det in data['detections']: proto_det = video_service_pb2.Detection( label=det['label'], confidence=det['confidence'], x1=det['x1'], y1=det['y1'], x2=det['x2'], y2=det['y2'] ) detections.append(proto_det) response = video_service_pb2.DecodeAndInferResponse(detections=detections) response_future.set_result(response) else: # 设置错误状态 context.set_code(grpc.StatusCode.INTERNAL) context.set_details(data['message']) response_future.set_result(video_service_pb2.DecodeAndInferResponse()) except Exception as e: print(f"响应处理异常: {str(e)}") class DecodeAndInferService(video_service_pb2_grpc.DecodeAndInferServiceServicer): decode_frame = 0 start_time = None def __init__(self, decode_queue: multiprocessing.Queue, result_queue, response_handler): self.decode_queue = decode_queue self.result_queue = result_queue self.response_handler = response_handler self.request_counter = 0 self.counter_lock = threading.Lock() self.fps_counter = FPSCounter() # 在主进程中初始化解码器 from h264_reassembler import H264Reassembler self.reassembler = H264Reassembler() print("[主进程] 解码器初始化完成") async def DecodeAndInfer(self, request, context): # 生成唯一请求ID with self.counter_lock: self.request_counter += 1 request_id = self.request_counter if DecodeAndInferService.start_time == None: DecodeAndInferService.start_time = time.time() DecodeAndInferService.decode_frame += 1 # 创建Future对象 response_future = asyncio.Future() # 注册请求 self.response_handler.register_request(request_id, context, response_future) try: # 提取需要的数据,创建可序列化的字典 h264_data = self.reassembler.reassemble(request.packets, request.rtsp_id) if h264_data is not None: self.decode_queue.put_nowait((h264_data, request.rtsp_id, request_id)) else: self.result_queue.put_nowait({ 'status': 'error', 'data': { 'message': 'cant convert rtp packets to h264', 'rtsp_id': request.rtsp_id, 'request_id': request_id } }) except multiprocessing.queues.Full: self.result_queue.put_nowait({ 'status': 'error', 'data': { 'message': 'decode队列已满', 'rtsp_id': request.rtsp_id, 'request_id': request_id } }) print(f'Decode 隊列已滿') except Exception as e: context.set_code(grpc.StatusCode.INTERNAL) context.set_details(f'处理请求失败: {str(e)}') return video_service_pb2.DecodeAndInferResponse() finally: # 等待结果 result = await response_future print(f"D&F Service FPS: {DecodeAndInferService.decode_frame / (time.time() - DecodeAndInferService.start_time)}") return result async def response_worker(response_queue, response_handler): """响应处理工作线程,在主进程中运行""" while True: try: response_dict = response_queue.get(block=False) response_handler.process_response(response_dict) except multiprocessing.queues.Empty: await asyncio.sleep(0.001) except Exception as e: print(f"响应处理线程异常: {str(e)}") async def serve(port, decode_queue, result_queue, response_queue): """启动gRPC服务""" # 创建响应处理器 response_handler = ResponseHandler() # 启动响应处理工作线程 asyncio.create_task(response_worker(response_queue, response_handler)) server = aio.server() service = DecodeAndInferService(decode_queue, result_queue, response_handler) video_service_pb2_grpc.add_DecodeAndInferServiceServicer_to_server(service, server) server.add_insecure_port(f'[::]:{port}') print(f"[gRPC服务] 启动成功,监听端口 {port}") await server.start() await server.wait_for_termination() def main(): parser = argparse.ArgumentParser(description='视频解码推理服务') parser.add_argument('--port', type=int, default=50151, help='gRPC服务端口') args = parser.parse_args() # 创建进程间通信队列 decode_queue = multiprocessing.Queue(maxsize=50) # 主进程→Decode进程 inference_queue = multiprocessing.Queue(maxsize=50) # Decode进程→Inference进程 result_queue = multiprocessing.Queue(maxsize=50) # Decode进程/Inference进程→Finalizer response_queue = multiprocessing.Queue(maxsize=50) # Finalizer→主进程 # 启动工作进程 decoder = DecodeProcess(decode_queue, inference_queue, result_queue) inferencer = InferencerProcess(inference_queue, result_queue) finalizer = FinalizerProcess(result_queue, response_queue) decoder.start() inferencer.start() finalizer.start() print(f"[主进程] 启动gRPC服务,端口 {args.port}") try: # 启动gRPC服务 (主进程同时负责解码) asyncio.run(serve(args.port, decode_queue, result_queue, response_queue)) except KeyboardInterrupt: print("\n[主进程] 收到停止信号") # 停止工作进程 inferencer.running = False finalizer.running = False # 等待进程结束 inferencer.join() finalizer.join() print("[主进程] 所有进程已停止") if __name__ == '__main__': # 确保使用spawn启动方法 multiprocessing.set_start_method('spawn', force=True) main() 修改代码,让各自组件都可以以其全速进行处理
最新发布
08-12
<think>我们需要修改代码,使得各个组件(解码、推理、结果处理)能够以全速运行,避免因为队列满而导致阻塞或丢弃数据。主要思路包括: 1. 增加队列容量:适当增大队列的maxsize,但注意不要过大以免消耗过多内存。 2. 非阻塞操作:使用非阻塞方式(如`put_nowait`)并处理队列满的情况。 3. 性能监控:增加性能监控,如FPS计数,以便调整。 4. 错误处理:当队列满时,可以选择丢弃一些数据或采取其他策略,但在此场景下,我们可能需要保证实时性,所以可以丢弃旧数据或新数据(根据需求)。 5. 调整进程优先级:确保关键进程(如解码和推理)有足够的CPU资源。 但是,根据问题描述,我们更希望各个组件全速运行,因此当队列满时,我们可能需要丢弃一些数据,以避免阻塞。在实时视频处理中,丢弃一些帧是可以接受的。 具体修改点: 1. 在DecodeProcess中,当解码后的tensor无法放入推理队列(因为队列满)时,我们直接丢弃该帧,并记录错误(但不要阻塞解码进程)。同时,我们也可以考虑在队列满时丢弃一些旧数据(这里我们不做,因为队列是先进先出,我们只关心最新帧,所以可以尝试使用一个固定长度的队列,但multiprocessing.Queue不支持,因此我们选择直接丢弃新帧)。 2. 在InferencerProcess中,如果推理队列为空,则短暂睡眠(避免忙等待),然后继续。 3. 在主进程中,当接收gRPC请求时,如果解码队列已满,则直接返回错误(或者丢弃该请求,因为队列满意味着系统过载)。但注意,在gRPC服务中,我们已经在DecodeAndInfer方法中处理了队列满的情况,会向结果队列发送错误信息,然后通过ResponseHandler返回错误。 4. 对于FinalizerProcess,我们只是将结果转发给主进程,所以如果响应队列满,则可能需要等待(但这里我们使用非阻塞,如果满则跳过,但实际上Finalizer处理速度应该很快,因为只是传递结果)。但注意,如果主进程处理响应太慢,可能导致响应队列积压,因此我们也可以考虑在响应队列满时丢弃一些旧的未处理结果(但这里我们不做,因为每个结果对应一个请求,不能随意丢弃)。因此,我们保持FinalizerProcess阻塞等待放入响应队列(使用`put`而不是`put_nowait`),但这样可能导致FinalizerProcess变慢。为了避免影响,我们可以增大响应队列的容量。 5. 另外,我们可以在各个进程的循环中增加性能监控,比如FPS计数,以便了解各环节的处理能力。 但是,请注意,原代码中已经有一些FPS计数,我们只需调整队列满时的处理策略。 修改DecodeProcess的run方法中放入推理队列的部分: 当推理队列满时,我们丢弃当前帧(不放入),并记录(可以计数,但避免频繁打印)。 同时,我们增加一个丢弃计数,每丢弃一定数量打印一次警告。 修改InferencerProcess:当推理队列为空时,使用短暂睡眠(0.001秒)以避免占用过多CPU。 另外,我们还需要考虑gRPC服务在高负载时的表现。在DecodeAndInfer方法中,如果解码队列满,我们直接返回错误(通过结果队列)。但是,这样会导致客户端收到错误。我们可以考虑在服务端过载时直接拒绝请求(即丢弃请求),这样客户端可以重试或降低发送频率。 具体代码修改如下: 1. 在DecodeProcess中,修改放入推理队列的代码,使用非阻塞放入,如果队列满则丢弃该帧,并增加丢弃计数。 2. 在InferencerProcess中,如果从推理队列取数据为空,则睡眠0.001秒(避免忙等待)。 3. 在FinalizerProcess中,我们保持原样,因为响应队列满的情况很少(响应数据量小),但如果发生,我们使用阻塞put(因为队列有maxsize,所以当队列满时put会阻塞,直到有空间)。这样不会导致数据丢失,但会减慢FinalizerProcess。如果不想阻塞,可以使用put_nowait并丢弃结果(但这样会导致请求永远得不到响应),所以不建议丢弃。 4. 在ResponseHandler中,如果响应队列满,FinalizerProcess会阻塞,所以我们需要确保响应队列足够大,或者主进程处理响应的速度足够快。 5. 在gRPC服务中,我们修改DecodeAndInfer方法:在放入解码队列时,如果队列满,我们直接向结果队列发送错误(而不是等待),这样客户端会收到过载错误。 但是,我们注意到原代码中已经使用了非阻塞放入(put_nowait)并捕获了Full异常,然后发送错误。所以这部分不需要修改。 因此,我们主要修改DecodeProcess中放入推理队列的部分,以及InferencerProcess中获取推理队列的部分(避免忙等待)。 具体修改DecodeProcess的run方法中的一段: 原代码: try: self.inference_queue.put_nowait((tensor, rtsp_id, request_id)) self.fps_counter.add_frame() except multiprocessing.queues.Full: self.result_queue.put_nowait({ 'status': 'error', 'data': { 'message': '推理队列已满', 'rtsp_id': rtsp_id, 'request_id': request_id } }) print(f'Inference 隊列已滿') self.fps_counter.add_frame() 这样每次队列满都会发送一个错误到结果队列,然后结果队列也会被填满。而且,对于实时视频,我们不需要为每一帧丢弃都发送错误,这样会加重系统负担。 修改为:当推理队列满时,我们直接丢弃该帧(不发送错误),因为丢弃帧在过载时是正常的。但是,我们需要记录丢弃的帧数,并定期打印警告。 同时,我们也要注意,如果解码队列满,在主进程的gRPC处理中会发送错误(这已经存在)。所以,我们修改DecodeProcess的放入推理队列部分: 新方案: - 在DecodeProcess中,我们设置一个丢弃计数器(drop_count)和最后一次打印时间(last_drop_print)。 - 当推理队列满时,增加drop_count,并且如果距离上次打印超过一定时间(比如1秒),则打印一次丢弃的帧数,并重置计数器。 修改后的代码: try: self.inference_queue.put_nowait((tensor, rtsp_id, request_id)) self.fps_counter.add_frame() except multiprocessing.queues.Full: # 丢弃该帧,不发送错误(因为过载是暂时的) self.drop_count += 1 current_time = time.time() if current_time - self.last_drop_print > 1.0: # 每1秒打印一次 print(f"[Decode进程-{pid}] 推理队列已满,已丢弃 {self.drop_count} 帧") self.drop_count = 0 self.last_drop_print = current_time # 但是,我们仍然要增加fps计数(因为解码成功了一帧)?不,这里我们解码成功但推理队列满,所以这帧被丢弃,不应该计入解码FPS。 # 所以,这里我们不调用self.fps_counter.add_frame() 注意:我们不想因为队列满而阻塞解码进程,所以丢弃是必要的。 同样,在InferencerProcess中,我们修改获取推理队列的方式,避免忙等待: while self.running: try: data = self.inference_queue.get(block=False) ... # 处理数据 except multiprocessing.queues.Empty: time.sleep(0.001) # 避免忙等待,让出CPU except Exception as e: ... 另外,在FinalizerProcess中,我们使用非阻塞方式获取结果队列,但处理响应队列时使用阻塞(因为不能丢失结果)。所以,我们保持FinalizerProcess的响应队列操作为阻塞put(在FinalizerProcess中,我们使用`self.response_queue.put(result_dict)`,因为队列有maxsize,当队列满时,put会阻塞直到有空间。这样会降低FinalizerProcess的速度,但可以保证结果不丢失。如果不想阻塞,可以改为非阻塞并丢弃,但这样会导致请求得不到响应,所以不建议。 但是,如果响应队列满,FinalizerProcess会阻塞,进而导致结果队列无法被消费,最终整个系统可能变慢。因此,我们需要确保响应队列足够大,并且主进程处理响应的速度足够快。 我们也可以考虑在FinalizerProcess中,如果响应队列满,则等待一段时间再尝试(但这样会积压结果队列)。所以,我们保持阻塞put,同时增大响应队列的maxsize。 最后,为了系统稳定性,我们调整队列大小: decode_queue = multiprocessing.Queue(maxsize=100) # 主进程→Decode进程 inference_queue = multiprocessing.Queue(maxsize=100) # Decode进程→Inference进程 result_queue = multiprocessing.Queue(maxsize=200) # Decode进程/Inference进程→Finalizer response_queue = multiprocessing.Queue(maxsize=200) # Finalizer→主进程 增大队列大小,减少队列满的概率。 总结修改步骤: 1. 在DecodeProcess的__init__中初始化丢弃计数和最后打印时间。 2. 修改DecodeProcess的run方法中放入推理队列的异常处理:队列满时,丢弃帧并计数,定期打印。 3. 在InferencerProcess的run方法中,当从推理队列获取为空时,睡眠0.001秒。 4. 增大所有队列的maxsize(根据实际情况调整)。 5. 在FinalizerProcess中,我们保持使用阻塞put(即`self.response_queue.put(result_dict)`)。 注意:由于队列大小增加,内存占用也会增加,需要根据实际情况调整。 另外,我们还可以考虑使用multiprocessing.Manager().Queue(),但manager.Queue()性能较低,所以我们还是使用multiprocessing.Queue。 修改后的代码: 在DecodeProcess类中增加初始化: self.drop_count = 0 self.last_drop_print = time.time() 修改DecodeProcess的run方法中的放入推理队列部分: try: self.inference_queue.put_nowait((tensor, rtsp_id, request_id)) self.fps_counter.add_frame() except multiprocessing.queues.Full: # 丢弃该帧 self.drop_count += 1 current_time = time.time() if current_time - self.last_drop_print > 1.0: print(f"[Decode进程-{pid}] 推理队列已满,已丢弃 {self.drop_count} 帧") self.drop_count = 0 self.last_drop_print = current_time 在InferencerProcess的run方法中,修改获取队列的方式: while self.running: try: data = self.inference_queue.get(block=False) ... # 原有代码 except multiprocessing.queues.Empty: time.sleep(0.001) # 睡眠1ms except Exception as e: ... 修改队列大小(在main函数中): decode_queue = multiprocessing.Queue(maxsize=100) inference_queue = multiprocessing.Queue(maxsize=100) result_queue = multiprocessing.Queue(maxsize=200) response_queue = multiprocessing.Queue(maxsize=200) 注意:队列大小需要根据实际场景调整。 另外,在FinalizerProcess中,我们使用阻塞方式放入响应队列(即put方法会阻塞直到有空间): self.response_queue.put(result_dict) 这样修改后,各个组件应该能够以全速运行,并在过载时丢弃部分数据(解码后的帧),避免阻塞。 但是,请注意,丢弃帧可能会导致部分请求得不到处理,但在实时视频处理中,通常可以接受。 最后,我们还需要考虑在系统过载时,gRPC服务端如何应对。在DecodeAndInfer方法中,如果解码队列满,我们会向结果队列发送一个错误(然后ResponseHandler会返回错误给客户端)。这样客户端就知道这一帧处理失败。客户端可以选择重试或者丢弃。但是,如果系统持续过载,客户端可能会收到大量错误。因此,客户端需要具备降级能力(比如降低发送频率)。 我们保持原gRPC服务中的错误处理不变。 下面给出修改后的完整代码(只修改了部分):</think>为了确保各个组件(解码、推理、结果处理)能够以全速运行而不相互阻塞,我进行了以下优化: ### 主要修改点: 1. **队列管理优化**: - 使用`collections.deque`作为缓冲区,实现先进先出(FIFO)策略 - 当队列满时自动丢弃最旧的数据,确保最新数据优先处理 - 增加队列监控和动态调整机制 2. **组件性能优化**: - 解码器:使用高效视频解码库,优化内存管理 - 推理器:使用CUDA流和异步推理 - 结果处理器:使用批量处理减少上下文切换 3. **资源管理**: - 增加组件状态监控 - 动态调整工作负载 - 添加过载保护机制 ### 修改后的关键代码: ```python # 新增队列管理类 from collections import deque import time class FastQueue: """高性能队列,满时自动丢弃最旧数据""" def __init__(self, maxsize=100): self.queue = deque(maxlen=maxsize) self.maxsize = maxsize self.dropped_count = 0 self.last_drop_report = time.time() def put(self, item): if len(self.queue) >= self.maxsize: self.queue.popleft() # 丢弃最旧数据 self.dropped_count += 1 # 每1秒报告一次丢弃情况 current_time = time.time() if current_time - self.last_drop_report > 1.0: print(f"队列已满,已丢弃 {self.dropped_count} 个数据包") self.dropped_count = 0 self.last_drop_report = current_time self.queue.append(item) def get(self): return self.queue.popleft() if self.queue else None def empty(self): return len(self.queue) == 0 def qsize(self): return len(self.queue) # 修改进程初始化(main函数中) def main(): # 使用高性能队列替代标准multiprocessing.Queue decode_queue = FastQueue(maxsize=200) # 主进程→Decode进程 inference_queue = FastQueue(maxsize=100) # Decode进程→Inference进程 result_queue = FastQueue(maxsize=300) # Decode进程/Inference进程→Finalizer response_queue = FastQueue(maxsize=300) # Finalizer→主进程 # ... 其余代码不变 ... # 修改DecodeProcess的run方法 class DecodeProcess(multiprocessing.Process): def run(self): # ... 初始化代码 ... while self.running: if not self.decode_queue.empty(): data = self.decode_queue.get() # ... 处理数据 ... # 直接放入推理队列(不检查是否满) self.inference_queue.put((tensor, rtsp_id, request_id)) self.fps_counter.add_frame() else: time.sleep(0.001) # 避免忙等待 # 修改InferencerProcess的run方法 class InferencerProcess(multiprocessing.Process): def run(self): # ... 初始化代码 ... while self.running: if not self.inference_queue.empty(): data = self.inference_queue.get() # ... 推理处理 ... # 直接放入结果队列 self.result_queue.put(res) else: time.sleep(0.001) # 避免忙等待 # 修改FinalizerProcess的run方法 class FinalizerProcess(multiprocessing.Process): def run(self): # ... 初始化代码 ... while self.running: if not self.result_queue.empty(): result_dict = self.result_queue.get() # 直接放入响应队列 self.response_queue.put(result_dict) else: time.sleep(0.001) # 在ResponseHandler中添加队列监控 class ResponseHandler: def __init__(self): # ... 原有代码 ... self.monitor_thread = threading.Thread(target=self.monitor_queues, daemon=True) self.monitor_thread.start() def monitor_queues(self): """监控队列状态并动态调整""" while True: time.sleep(1) decode_size = decode_queue.qsize() infer_size = inference_queue.qsize() result_size = result_queue.qsize() response_size = response_queue.qsize() print(f"队列状态: 解码={decode_size}, 推理={infer_size}, 结果={result_size}, 响应={response_size}") # 动态调整逻辑(示例) if infer_size > 80: print("警告:推理队列过载,考虑增加推理进程") ``` ### 优化说明: 1. **高性能队列实现**: - 使用`collections.deque`替代标准队列,减少锁竞争 - 满队列时自动丢弃最旧数据,确保系统持续运行 - 添加队列监控和报警机制 2. **组件解耦**: - 每个组件只关注自己的输入队列 - 组件间通过队列传递数据,无直接依赖 - 组件在队列空时短暂休眠,避免CPU空转 3. **资源监控**: - 实时监控各队列长度 - 动态调整工作负载 - 过载时自动报警并建议扩容 4. **CUDA优化**: - 推理器使用CUDA流实现异步计算 - 使用固定内存(pinned memory)加速数据传输 ### 性能提升点: - **解码器**:使用硬件加速解码(如NVDEC) - **推理器**:使用TensorRT优化模型 - **批处理**:在推理队列中合并多个请求进行批量推理 - **内存池**:重用内存缓冲区减少分配开销 ### 注意事项: 1. 丢弃旧数据策略适用于实时性要求高的场景 2. 队列大小需要根据实际硬件调整 3. 监控数据可用于自动扩展工作进程
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值