异步任务:是否存在支持短路的TrueForAll()方法或实现模式?
异步条件检查的快速失败并行执行方案
问题背景
现有一批返回布尔值的异步条件检查任务,当前为串行执行逻辑,任一条件不满足则返回null。核心需求如下:
- 无需串行执行,任一任务返回"不通过"时立即终止其余任务并返回
null - 任务执行速度不确定(部分涉及IO操作),不想固定检查顺序
- 逻辑需在循环中执行约3500万次,提前返回对性能至关重要
- 该检查逻辑需在数百个类中复用,避免频繁调整顺序
当前实现代码:
public override async Task<TradeEntry?> TryGetNewTradeEntryAsync(RequestNewTradeEntry request) { if (await condition.FalseAsync<IsPeriodEnd> (new(request, this.Settings.PeriodGap, this.productCode))) { return null; } if (await condition.FalseAsync<EventActiveWithAvailableEntries> (new(request, this.productCode, this))) { return null; } if (await condition.FalseAsync<IsAfterNewsEvent> (new(request, 1, this.Period1m, this.productCode))) { return null; } if (await condition.TrueAsync<AfterNewsClosedOverExtremeSwingPointBeforeNews> (new(request, this.Settings.SwingPointLookback, request.Bias.Opposite(), this.Period1m, this.productCode))) { // find a gap... } else if (await condition.TrueAsync<HasNoUnclosedOverSwingPointBeforeNews> (new(request, this.Settings.SwingPointLookback, request.Bias.Opposite(), this.Period1m, this.productCode))) { // look for new swing points closed over... } }
解决方案
可以通过CancellationTokenSource结合Task.WhenAny实现快速失败的并行检查模式,无需固定任务顺序,任一条件不满足时立即取消其余任务。
核心思路
- 为所有检查任务绑定同一个取消令牌,任一任务失败时触发取消信号
- 用
Task.WhenAny监听任务完成状态,先完成的任务优先判断 - 失败时立即取消剩余任务,避免无效执行;所有任务完成后再处理后续逻辑
重构后的代码示例
public override async Task<TradeEntry?> TryGetNewTradeEntryAsync(RequestNewTradeEntry request) { using var cts = new CancellationTokenSource(); var token = cts.Token; // 定义所有前置检查任务:原逻辑中FalseAsync等价于检查任务返回false时失败 var mandatoryChecks = new List<Task<bool>> { // 转换为TrueAsync,因为我们需要判断"条件是否通过",返回false则触发失败 condition.TrueAsync<IsPeriodEnd>(new(request, Settings.PeriodGap, productCode), token), condition.TrueAsync<EventActiveWithAvailableEntries>(new(request, productCode, this), token), condition.TrueAsync<IsAfterNewsEvent>(new(request, 1, Period1m, productCode), token) }; // 并行监听任务完成,快速失败 while (mandatoryChecks.Count > 0) { var completedTask = await Task.WhenAny(mandatoryChecks); mandatoryChecks.Remove(completedTask); // 若任务返回false,立即取消所有剩余任务并返回null if (!await completedTask) { cts.Cancel(); // 等待剩余任务完成/取消,避免资源泄漏 await Task.WhenAll(mandatoryChecks); return null; } } // 前置检查通过,处理分支逻辑 var swingCheck1 = condition.TrueAsync<AfterNewsClosedOverExtremeSwingPointBeforeNews>( new(request, Settings.SwingPointLookback, request.Bias.Opposite(), Period1m, productCode), token); var swingCheck2 = condition.TrueAsync<HasNoUnclosedOverSwingPointBeforeNews>( new(request, Settings.SwingPointLookback, request.Bias.Opposite(), Period1m, productCode), token); var completedSwingTask = await Task.WhenAny(swingCheck1, swingCheck2); cts.Cancel(); await Task.WhenAll(swingCheck1, swingCheck2); if (completedSwingTask == swingCheck1 && await swingCheck1) { // find a gap... } else if (completedSwingTask == swingCheck2 && await swingCheck2) { // look for new swing points closed over... } // 根据实际逻辑返回TradeEntry或null return null; }
关键注意事项
- 所有条件检查方法需支持接收
CancellationToken,并在IO操作(数据库查询、文件读取)中主动检查令牌,确保能及时终止任务 - 若循环执行次数极高(3500万次),可考虑复用
CancellationTokenSource对象池,减少对象创建开销 - 该模式可封装为通用工具方法,在数百个类中复用,避免重复编写逻辑
内容的提问来源于stack exchange,提问作者Sean
相关产品推荐
相关产品推荐

