You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Task.WaitAll聚合异常处理:批量API请求成功结果处理遇异常

问题:批量处理异步任务时遍历触发未捕获AggregateException

我开发了一个按100条为一批处理结果的程序,每条数据单独发起API请求后批量处理返回结果。近期调用的API出现扩容问题,约10%的请求会随机失败或超时。

我希望仅处理请求成功的结果,但在遍历Task数组时,会触发新的AggregateException(其InnerExceptions仅包含一个异常),仿佛触碰到了Task数组中的“定时炸弹”成员,却无法定位原因。

外层异常捕获逻辑:

try
{
    GetBatchStats();
}
catch (AggregateException e)
{
    var firstE = e.InnerExceptions[0];
    _logger.Fatal($"{e.InnerExceptions.Count.ToString()} api requests threw exceptions.  Sample exception: {firstE.Message}\nStackTrace: {firstE.StackTrace}");
}

最初的批量处理逻辑:

int asyncExceptions = 0;
try
{
    Task.WaitAll(batchRequests);
}
catch(AggregateException e)
{
    asyncExceptions = e.InnerExceptions.Count;
    var firstE = e.InnerExceptions[0];
    _logger.Error($"{asyncExceptions.ToString()} api requests out of {batchRequests.Length.ToString()} threw exceptions.  Sample exception: {firstE.Message}\nStackTrace: {firstE.StackTrace}");
}

if (asyncExceptions == batchRequests.Length)
{   // All requests in this batch errored out; api must be down
    throw new Exception("All api requests in batch returned errors.  Must be a problem with the api.");
}

int countErrs = 0;
foreach (var apiResult in batchRequests)
{
    if (apiResult.Exception != null || apiResult.Result == null) countErrs++;

    if (apiResult.Exception == null && apiResult.Result != null)
    { ...
    }
}

日志显示先输出“Y条请求中有X条抛出异常”的错误,紧接着出现“1条请求抛出异常”的致命信息,说明:

  • 程序进入了处理成功结果的循环;
  • 访问Task数组中的某个成员时触发了新的AggregateException,但无法定位原因。

改用Task.Status判断后问题依旧:

int asyncExceptions = 0;
try
{
    Task.WaitAll(batchRequests);
}
catch(AggregateException e)
{
    asyncExceptions = e.InnerExceptions.Count;
    var firstE = e.InnerExceptions[0];
    _logger.Error($"{asyncExceptions.ToString()} api requests out of {batchRequests.Length.ToString()} threw exceptions.  Sample exception: {firstE.Message}\nStackTrace: {firstE.StackTrace}");
}

if (asyncExceptions == batchRequests.Length)
{   // All requests in this batch errored out; api must be down
    throw new Exception("All api requests in batch returned errors.  Must be a problem with the api.");
}

int countErrs = 0;
foreach (var apiResult in batchRequests)
{
    if (apiResult.Status == TaskStatus.Faulted || apiResult.Result == null) countErrs++;

    if (apiResult.Status == TaskStatus.RanToCompletion && apiResult.Result != null)
    { ...
    }
}

求问:如何正确处理一组任务中成功的结果,同时跳过错误的任务?


解决方案

问题核心是:当任务处于Faulted状态时,访问其Result属性会自动抛出该任务的异常——哪怕你已经通过Task.WaitAll捕获过一次AggregateException,后续访问Faulted任务的Result依然会触发未捕获的异常。

修正同步处理逻辑

先通过Task.Status判断任务状态,仅对RanToCompletion状态的任务访问Result,直接跳过错误任务:

int asyncExceptions = 0;
try
{
    Task.WaitAll(batchRequests);
}
catch(AggregateException e)
{
    asyncExceptions = e.InnerExceptions.Count;
    var firstE = e.InnerExceptions[0];
    _logger.Error($"{asyncExceptions} api requests out of {batchRequests.Length} threw exceptions.  Sample exception: {firstE.Message}\nStackTrace: {firstE.StackTrace}");
}

if (asyncExceptions == batchRequests.Length)
{
    throw new Exception("All api requests in batch returned errors.  Must be a problem with the api.");
}

int countErrs = 0;
foreach (var apiResult in batchRequests)
{
    // 先判断任务状态,避免访问Result触发异常
    if (apiResult.Status != TaskStatus.RanToCompletion)
    {
        countErrs++;
        continue;
    }

    // 仅处理成功完成的任务
    var result = apiResult.Result;
    if (result == null)
    {
        countErrs++;
        continue;
    }

    // 这里写处理result的业务逻辑
    // ...
}

推荐:改用异步/await模式

异步模式的异常处理更直观,每个任务的异常可单独捕获,不会影响其他任务:

// 为每个任务单独包裹异常捕获逻辑
var wrappedTasks = batchRequests.Select(async task => 
{
    try
    {
        var result = await task;
        return (IsSuccess: true, Result: result);
    }
    catch (Exception ex)
    {
        _logger.Error($"API请求失败: {ex.Message}\nStackTrace: {ex.StackTrace}");
        return (IsSuccess: false, Result: default);
    }
}).ToList();

// 等待所有任务完成,无论成功失败
var results = await Task.WhenAll(wrappedTasks);

int countErrs = 0;
foreach (var res in results)
{
    if (!res.IsSuccess || res.Result == null)
    {
        countErrs++;
        continue;
    }

    // 处理成功的结果
    // ...
}

if (countErrs == batchRequests.Length)
{
    throw new Exception("All api requests in batch returned errors.  Must be a problem with the api.");
}

这种方式下,遍历结果时不会触发意外异常,代码可读性和可维护性更强。

内容的提问来源于stack exchange,提问作者user1664043

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.07 02:17:11