定时器、任务与线程咨询:多摄像头API调用异步执行优化
问题分析与解决方案
你的核心需求是为4路摄像头各自维护串行的API调用队列——每路摄像头的下一次API调用必须等待上一次完成后再执行,同时4路之间可以并行处理。当前代码的问题在于:定时器每200ms会强制触发新的一批4个任务,即便上一批任务还未完成,最终会导致多批次任务并发堆积,完全无法实现“排队等待”的效果,Task.WhenAll仅能等待当前批次的任务完成,无法阻止定时器的下一次触发。
最优实现方案:为每路摄像头独立维护串行任务链
我们需要为每个摄像头单独记录当前正在执行的任务,每次定时器触发时,将新的API调用追加到对应摄像头的任务链中,确保前一次任务完成后才执行下一次。
代码实现示例
// 为每路摄像头维护当前执行的任务,初始为已完成状态 private Task _camera1Task = Task.CompletedTask; private Task _camera2Task = Task.CompletedTask; private Task _camera3Task = Task.CompletedTask; private Task _camera4Task = Task.CompletedTask; // 线程安全锁:定时器回调可能在不同线程触发,确保任务变量更新时的原子性 private readonly object _taskLock = new object(); private void OnTimerTickFront(object? sender, EventArgs e) { lock (_taskLock) { // 为每路摄像头追加新的API调用任务 _camera1Task = QueueCameraTask(_camera1Task, Method1Async); _camera2Task = QueueCameraTask(_camera2Task, Method2Async); _camera3Task = QueueCameraTask(_camera3Task, Method3Async); _camera4Task = QueueCameraTask(_camera4Task, Method4Async); } } private async Task QueueCameraTask(Task previousTask, Func<Task> apiCall) { // 等待前一次任务完成 await previousTask.ConfigureAwait(false); try { // 执行当前API调用(如果是IO密集型API,无需Task.Run包装) await apiCall().ConfigureAwait(false); } catch (Exception ex) { // 必须捕获异常:避免单次API调用失败导致整个任务链中断 // 示例:记录日志 // Logger.Error($"摄像头API调用失败: {ex.Message}"); } }
关键细节说明
- 独立串行队列:每路摄像头的任务链完全独立,某一路API响应慢不会影响其他路的任务排队,同时保证每路的调用严格按顺序执行。
- 线程安全:使用
lock确保任务变量的更新不会被并发线程干扰,避免任务链断裂。 - 异常隔离:在
QueueCameraTask中捕获异常,防止单次API调用失败导致后续所有排队任务无法执行。 - 避免
async void:定时器回调改为void类型,async void的异常无法被常规方式捕获,容易导致程序崩溃。 - API调用优化:如果你的
MethodXAsync本身是IO密集型(比如网络请求、摄像头数据读取),不需要用Task.Run包装,直接调用异步方法即可;如果是CPU密集型任务,再考虑用Task.Run将任务放到线程池。
为什么不推荐原方案?
原代码中Task.WhenAll仅能等待当前批次的4个任务完成,但定时器会继续按200ms间隔触发新的回调,最终会导致多个批次的任务同时运行,不仅无法实现排队,还可能因为并发量过大导致API服务压力过载,或本地资源耗尽。
内容的提问来源于stack exchange,提问作者Mike
相关产品推荐
相关产品推荐

