如何对未被等待的任务执行try/catch/finally清理操作?
如何对未被等待的任务执行try/catch/finally清理操作?
咱们先看你这段代码的问题:你启动了readTask和writeTask两个后台任务,但如果后面的DoSomeNonAsyncWork或者MaybeSomeOtherAsyncWork抛出异常,这两个子任务可能还在后台瞎跑,不仅浪费资源,在旧版.NET里甚至会因为未观察到的异常搞崩程序。
要解决这个问题,核心就是主流程出问题时,先通知子任务停止,再等待它们完成,最后统一处理异常和清理资源。我给你调整后的代码如下:
public async Task Handle(HttpContext httpContext, WebSocket socket) { using var cts = CancellationTokenSource.CreateLinkedTokenSource(httpContext.RequestAborted); Task readTask = null; Task writeTask = null; try { readTask = Run(socket, cts.Token); writeTask = Run2(socket, cts.Token); DoSomeNonAsyncWork(cts.Token); // 同步方法抛异常会直接进catch await MaybeSomeOtherAsyncWork(cts.Token); // 异步方法抛异常也会进catch // 主流程正常走完,等待子任务全部完成 await Task.WhenAll(readTask, writeTask); } catch (Exception mainEx) { // 主流程出问题了,先给子任务发取消信号 cts.Cancel(); // 必须等待子任务结束,不然会留下未观察异常的隐患 if (readTask != null && writeTask != null) { try { await Task.WhenAll(readTask, writeTask); } catch (Exception taskEx) { // 这里处理子任务抛出的异常,比如打日志或者合并到主异常里 Console.WriteLine($"子任务执行异常:{taskEx.Message}"); } } // 把主流程的异常抛出去,或者在这里统一做业务处理 throw new InvalidOperationException("主流程执行失败", mainEx); } finally { // 兜底的资源清理,确保WebSocket被正确关闭 if (socket.State == WebSocketState.Open || socket.State == WebSocketState.CloseSent) { await socket.CloseAsync( WebSocketCloseStatus.NormalClosure, "操作结束,资源已清理", CancellationToken.None); } } }
再给你划几个必须注意的点:
- 别让子任务“失控”:只要启动了任务,哪怕没立刻await,也要在异常发生时主动取消并等待完成,不然资源泄漏和未观察异常的坑迟早要踩
- CancelToken要串到底:你已经把
cts和httpContext.RequestAborted关联了,这样客户端取消请求时子任务也会收到信号,这一点做得不错,要保持 - finally做最后兜底:不管主流程是正常完成还是异常退出,finally块的清理逻辑都会执行,比如关闭WebSocket,这是避免资源泄漏的关键
- 异常分层处理:主流程和子任务的异常可以分开处理,也可以合并成一个异常抛出,完全看你的业务需求
内容来源于stack exchange
相关产品推荐
相关产品推荐

