[英]AggregateException from Task.WhenAll only contains first exception when awaited
When causing multiple exceptions in a Task.WhenAll
call, it looks like only one of the exceptions is absorbed into the Task once you await it through more than one layer of awaiting.在
Task.WhenAll
调用中导致多个异常时,一旦您通过多个等待层等待它,看起来只有一个异常被吸收到 Task 中。 I was under the impression that the Task.Exception.InnerExceptions
property would contain all exceptions that occurred, but under certain circumstances they seem to only have one.我的印象是
Task.Exception.InnerExceptions
属性将包含所有发生的异常,但在某些情况下它们似乎只有一个。
For example, this sample code creates multiple exception-throwing Tasks and then awaits a Task.WhenAll on them, and then writes to console the exceptions that it is able to catch:例如,此示例代码创建多个引发异常的任务,然后在它们上等待一个 Task.WhenAll,然后写入控制台它能够捕获的异常:
class Program
{
static async Task Main(string[] args)
{
var task = CauseMultipleExceptionsAsync();
// Delaying until all the Exceptions have been thrown, ensuring it isn't just a weird race condition happening behind the scenes
await Task.Delay(5000);
try
{
await task;
}
catch(AggregateException e)
{
// This does not get hit
Console.WriteLine($"AggregateException caught: Found {e.InnerExceptions.Count} inner exception(s)");
}
catch(Exception e)
{
Console.WriteLine($"Caught other Exception {e.Message}");
Console.WriteLine($"task.Exception.InnerExceptions contains {task.Exception.InnerExceptions.Count} exception(s)");
foreach (var exception in task.Exception.InnerExceptions)
{
Console.WriteLine($"Inner exception {exception.GetType()}, message: {exception.Message}");
}
}
}
static async Task CauseMultipleExceptionsAsync()
{
var tasks = new List<Task>()
{
CauseExceptionAsync("A"),
CauseExceptionAsync("B"),
CauseExceptionAsync("C"),
};
await Task.WhenAll(tasks);
}
static async Task CauseExceptionAsync(string message)
{
await Task.Delay(1000);
Console.WriteLine($"Throwing exception {message}");
throw new Exception(message);
}
}
I was expecting this to either enter the catch(AggregateException e)
clause, or at least to have three inner exceptions in task.Exception.InnerExceptions
- what actually happens that one one exception is raised, and only only one of the exceptions is in task.Exception.InnerExceptions
:我期望这要么进入
catch(AggregateException e)
子句,要么至少在task.Exception.InnerExceptions
中有三个内部异常 - 实际上会发生一个异常,并且只有一个异常在task.Exception.InnerExceptions
中task.Exception.InnerExceptions
:
Throwing exception B
Throwing exception A
Throwing exception C
Caught other Exception A
task.Exception.InnerExceptions contains 1 exception(s)
Inner exception System.Exception, message: A
What is weirder is that this behaviour changes depending on whether you await the Task.WhenAll
call in CauseMultipleExceptionsAsync
- if you return the Task directly rather than awaiting it, then all three exceptions appear in task.Exception.InnerException
.更奇怪的是,这种行为会根据您是否在
CauseMultipleExceptionsAsync
中等待Task.WhenAll
调用而改变 - 如果您直接返回 Task 而不是等待它,那么所有三个异常都会出现在task.Exception.InnerException
中。 For instance, replacing CauseMultipleExceptionsAsync
with this:例如,将
CauseMultipleExceptionsAsync
替换为:
static Task CauseMultipleExceptionsAsync()
{
var tasks = new List<Task>()
{
CauseExceptionAsync("A"),
CauseExceptionAsync("B"),
CauseExceptionAsync("C"),
};
return Task.WhenAll(tasks);
}
Gives this result, with all three exceptions contained in task.Exception.InnerExceptions:给出这个结果,task.Exception.InnerExceptions 中包含所有三个异常:
Throwing exception C
Throwing exception A
Throwing exception B
Caught other Exception A
task.Exception.InnerExceptions contains 3 exception(s)
Inner exception System.Exception, message: A
Inner exception System.Exception, message: B
Inner exception System.Exception, message: C
I'm quite confused about this - where did exceptions B and C go in the initial example?我对此感到很困惑 - 在最初的示例中,异常 B 和 C go 在哪里? How would you go about finding them again if Task.Exception doesn't contain any information about them?
如果 Task.Exception 不包含有关它们的任何信息,您将如何再次找到它们? Why does awaiting inside
CauseMultipleExceptionsAsync
hide these exceptions, while returning the Task.WhenAll
directly does not?为什么在
CauseMultipleExceptionsAsync
中等待会隐藏这些异常,而直接返回Task.WhenAll
则不会?
If it makes a difference, I am able to replicate the above in both.Net Framework 4.5.2 and.Net Core 2.1.如果它有所作为,我可以在 .Net Framework 4.5.2 和 .Net Core 2.1 中复制上述内容。
What you are observing is the behavior of the await
operator, not the behavior of the Task.WhenAll
method.您正在观察的是
await
运算符的行为,而不是Task.WhenAll
方法的行为。 If you are interested in why the await
behaves this way, you could read this article from the early days of async/await:如果你对
await
为什么会这样表现感兴趣,你可以阅读 async/await 早期的这篇文章:
Having the choice of always throwing the first or always throwing an aggregate, for
await
we opt to always throw the first.可以选择总是抛出第一个或总是抛出一个聚合,对于
await
我们选择总是抛出第一个。 This doesn't mean, though, that you don't have access to the same details.但这并不意味着您无法访问相同的详细信息。 In all cases, the Task's Exception property still returns an
AggregateException
that contains all of the exceptions, so you can catch whichever is thrown and go back to consultTask.Exception
when needed.在所有情况下,Task 的 Exception 属性仍会返回包含所有异常的
AggregateException
,因此您可以捕获抛出的任何异常,并在需要时返回 go 以查阅Task.Exception
。 Yes, this leads to a discrepancy between exception behavior when switching betweentask.Wait()
andawait task
, but we've viewed that as the significant lesser of two evils.是的,这会导致在
task.Wait()
和await task
之间切换时的异常行为之间存在差异,但我们认为这是两害相权取其轻。
In case you would like to implement a method similar in behavior to Task.WhenAll
, but without losing the convenience of the async/await machinery, it is tricky but there are workarounds available here .如果您想实现与
Task.WhenAll
行为类似的方法,但又不会失去 async/await 机制的便利性,这很棘手,但这里有可用的解决方法。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.