处理异步并行任务中的多个异常 [英] Handling multiple exceptions from async parallel tasks

查看:69
本文介绍了处理异步并行任务中的多个异常的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

问题

几个任务是并行运行的,所有任务都不执行,或者其中任何一个都可能引发异常.完成所有任务后,必须报告所有可能发生的异常(通过日志,电子邮件,控制台输出....等等).

Several tasks are run in parallel, and all, none, or any of them might throw exceptions. When all the tasks have finalized, all the exceptions that might have happened must be reported (via log, email, console output.... whatever).

预期的行为

我可以通过带有异步lambda的linq构建所有任务,然后等待它们与 Task.WhenAll(tasks)并行运行.然后,我可以捕获一个 AggregateException 并报告每个单独的内部异常.

I can build all the tasks via linq with async lambdas, and then await for them running in parallel with Task.WhenAll(tasks). Then I can catch an AggregateException and report each of the individual inner exceptions.

实际行为

引发了 AggregateException ,但它仅包含一个内部异常,无论已引发了多少单个异常.

An AggregateException is thrown, but it contains just one inner exception, whatever number of individual exceptions have been thrown.

最少完整的可验证示例

static void Main(string[] args)
{
    try
    {
        ThrowSeveralExceptionsAsync(5).Wait();
    }
    catch (AggregateException ex)
    {
        ex.Handle(innerEx =>
        {
            Console.WriteLine($"\"{innerEx.Message}\" was thrown");
            return true;
        });
    }

    Console.ReadLine();
}

private static async Task ThrowSeveralExceptionsAsync(int nExceptions)
{
    var tasks = Enumerable.Range(0, nExceptions)
        .Select(async n =>
        {
            await ThrowAsync(new Exception($"Exception #{n}"));
        });

    await Task.WhenAll(tasks);
}

private static async Task ThrowAsync(Exception ex)
{
    await Task.Run(() => {
        Console.WriteLine($"I am going to throw \"{ex.Message}\"");
        throw ex;
    });
}

输出

请注意,由于比赛条件的原因,我要扔"消息的输出顺序可能会发生变化.

Note that the output order of the "I am going to throw" messages might change, due to race conditions.

I am going to throw "Exception #0"
I am going to throw "Exception #1"
I am going to throw "Exception #2"
I am going to throw "Exception #3"
I am going to throw "Exception #4"
"Exception #0" was thrown

推荐答案

这是因为 await "unwraps"聚合异常并始终仅引发第一个异常(如

That's because await "unwraps" aggregate exceptions and always throws just first exception (as described in documentation of await), even when you await Task.WhenAll which obviously can result in multiple errors. You can access aggregate exception for example like this:

var whenAll = Task.WhenAll(tasks);
try {
    await whenAll;
}
catch  {
    // this is `AggregateException`
    throw whenAll.Exception;
}

或者您也可以遍历任务并检查每个任务的状态和异常.

Or you can just loop over tasks and check status and exception of each.

请注意,在该修复程序之后,您需要再做一件事:

Note that after that fix you need to do one more thing:

try {
    ThrowSeveralExceptionsAsync(5).Wait();
}
catch (AggregateException ex) {
    // flatten, unwrapping all inner aggregate exceptions
    ex.Flatten().Handle(innerEx => {
        Console.WriteLine($"\"{innerEx.Message}\" was thrown");
        return true;
    });
}

因为 ThrowSeveralExceptionsAsync 返回的任务包含我们抛出的 AggregateException ,并包裹在另一个 AggregateException 中.

Because task returned by ThrowSeveralExceptionsAsync contains AggregateException we thrown, wrapped inside another AggregateException.

这篇关于处理异步并行任务中的多个异常的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆