今天我和我的同事讨论了如何正确处理C#5.0 async
方法中的异常,我们想知道是否同时等待多个任务也会观察到运行时没有解开的异常。
请考虑以下代码段:
async Task ExceptionMethodAsync()
{
await Task.Yield();
throw new Exception();
}
async Task CallingMethod()
{
try
{
var a = ExceptionMethodAsync();
var b = ExceptionMethodAsync();
await Task.WhenAll(a, b);
}
catch(Exception ex)
{
// Catches the "first" exception thrown (whatever "first" means)
}
}
现在第二项任务会怎样?两者都将处于故障状态,但现在是观察到或未观察到的第二个任务例外?
答案 0 :(得分:32)
Task.WhenAll
返回一个任务,就像Exception
属性包含AggregateException
并结合所有异常的所有任务一样。
当你await
这样的任务时,实际上只会抛出第一个异常。
...无论是因为子任务出错,还是因为像Task.WhenAlll这样的组合器,单个任务可能代表多个操作,而且多个操作可能有故障。在这种情况下,并且目标是不丢失异常信息(这对于事后调试很重要),我们希望能够表示多个异常,因此对于包装器类型,我们选择了AggregateException。
......考虑到这一点,并且再次选择总是抛出第一个或总是抛出一个聚合,为了“等待”我们选择总是抛出第一个
来自Task Exception Handling in .NET 4.5
由您决定是否要使用await task;
(大多数情况下为真)处理第一个或使用task.Exception
处理所有内容(如下例所示),但在这两种情况下,a
和b
都不会引发UnobservedTaskException
。
var task = Task.WhenAll(a, b);
try
{
await task;
}
catch
{
Trace.WriteLine(string.Join(", ", task.Exception.Flatten().InnerExceptions.Select(e => e.Message)));
}