如何按顺序执行多个异步任务并在异常时继续执行?
异步任务顺序执行的异常处理问题
我希望按顺序执行多个异步任务,初始写法如下:
foreach (var msg in messages) { await smtp.SendAsync(msg); }
但我希望当其中某个任务失败时,其余任务仍能继续执行,并在最后抛出一个异常。这类似于WhenAll()的行为——执行所有任务,若有失败则在最后抛出AggregateException,但我找不到对应的顺序执行替代方案。
我查阅了文档并搜索相关内容后,未发现内置实现方式,于是手动处理异常,写法如下:
var exceptions = new List<Exception>(); foreach (var msg in messages) { try { await smtp.SendAsync(msg).ConfigureAwait(false); } catch (Exception ex) { exceptions.Add(ex); } } if (exceptions.Count > 0) { throw new AggregateException(exceptions); }
现提出以下问题:
- 是否存在我遗漏的.NET内置解决方案?
- 若没有内置方案,当前写法是否正确?使用
ContinueWith链式调用并返回最终的“链式任务”是否更优(从而避免await/async带来的不必要状态机构建)? - 我是否可以使用.NET内置的
AggregateException,这是否属于不良实践?
回答
1. 不存在对应的内置解决方案
.NET没有提供和WhenAll()功能等价的顺序执行内置API。WhenAll()的核心是并行执行所有任务后汇总异常,而顺序执行的场景需要手动处理,你当前的思路是正确的。
2. 当前写法正确,ContinueWith无显著优势
- 写法正确性:这段代码完全符合需求——按顺序执行每个异步任务,捕获所有执行异常,最后统一抛出
AggregateException。ConfigureAwait(false)的使用也合理,能避免不必要的上下文切换(适用于不需要同步回原上下文的场景)。 ContinueWith对比:用ContinueWith链式构建任务确实可以规避async/await的状态机,但会大幅降低代码可读性。async/await的状态机开销在绝大多数业务场景下可以忽略,而代码的可维护性远重于这点微小的性能差异。除非你处于极端性能敏感的场景(比如每秒上万次的高频执行逻辑),否则完全没必要用ContinueWith替代当前写法。
3. 可以使用AggregateException,不属于不良实践
AggregateException本身就是为汇总多个异常的场景设计的,.NET自身的API(比如WhenAll())也会抛出该类型异常。只要你的场景需要集中反馈多个执行失败的异常,使用它完全合理,不属于不良实践。只需注意抛出时确保异常列表完整,不要遗漏捕获到的异常即可。
内容的提问来源于stack exchange,提问作者Alex from Jitbit
相关产品推荐
相关产品推荐

