为什么不等待Task.WhenAll抛出一个AggregateException?

在这个代码中:

private async void button1_Click(object sender, EventArgs e) { try { await Task.WhenAll(DoLongThingAsyncEx1(), DoLongThingAsyncEx2()); } catch (Exception ex) { // Expect AggregateException, but got InvalidTimeZoneException } } Task DoLongThingAsyncEx1() { return Task.Run(() => { throw new InvalidTimeZoneException(); }); } Task DoLongThingAsyncEx2() { return Task.Run(() => { throw new InvalidOperation();}); } 

我期待WhenAll创build并抛出一个AggregateException ,因为至less有一个它正在等待的任务抛出一个exception。 相反,我收回了其中一个任务抛出的单个exception。

WhenAll不总是创build一个AggregateException

我不记得在哪里,但我读了一些新的asynchronous/等待关键字,他们把AggregateException解包成实际的exception。

所以,在catch块中,你会得到实际的exception,而不是汇总的exception。 这有助于我们编写更自然,直观的代码。

这也是为了更容易地将现有代码转换为使用async / await ,其中大量的代码需要特定的exception而不是聚合的exception。

– 编辑 –

得到它了:

比尔·瓦格纳的一个asynchronous入门

比尔·瓦格纳(Bill Wagner)说:(在例外发生时

…使用await时,编译器生成的代码将展开AggregateException并引发基础exception。 通过利用await,可以避免额外的工作来处理由Task.Result,Task.Wait和Task类中定义的其他Wait方法所使用的AggregateExceptiontypes。 这是使用await而不是基本的任务方法的另一个原因….

你正在考虑Task.WaitAll – 它抛出一个AggregateException

什么时候只会抛出遇到的exception列表的第一个exception。

您可以遍历所有任务以查看是否有多个抛出exception:

 private async void button1_Click(object sender, EventArgs e) { var tasks = new [] {DoLongThingAsyncEx1(), DoLongThingAsyncEx2()}; try { await Task.WhenAll(tasks); } catch (Exception ex) { var exceptions = tasks.Where(t => t.Exception != null) .Select(t => t.Exception); } } Task DoLongThingAsyncEx1() { return Task.Run(() => { throw new InvalidTimeZoneException(); }); } Task DoLongThingAsyncEx2() { return Task.Run(() => { throw new InvalidOperation();}); } 

我知道这是一个已经回答的问题,但是所select的答案并不能真正解决OP的问题,所以我想我会发布这个问题。

此解决scheme为您提供聚合exception(即各种任务引发的所有exception)并且不会阻塞(工作stream仍然是asynchronous的)。

 async Task Main() { var task = Task.WhenAll(A(), B()); try { var results = await task; Console.WriteLine(results); } catch (Exception) { } if (task.Exception != null) { throw task.Exception; } } public async Task<int> A() { await Task.Delay(100); throw new Exception("A"); } public async Task<int> B() { await Task.Delay(100); throw new Exception("B"); } 

关键是在您等待之前保存对聚合任务的引用,然后可以访问其持有AggregateException(即使只有一个任务引发exception)的Exception属性。

希望这仍然有用。 我知道我今天有这个问题。

在您的代码中,第一个exception由devise返回,如http://blogs.msdn.com/b/pfxteam/archive/2011/09/28/task-exception-handling-in-net-4-5中所述。; ASPX

至于你的问题,如果你写这样的代码,你会得到AggreateException:

 try { var result = Task.WhenAll(DoLongThingAsyncEx1(), DoLongThingAsyncEx2()).Result; } catch (Exception ex) { // Expect AggregateException here } 

试试这个代码模式:

 Task task = null; try { task = Task.WhenAll(...); await task; } catch (AggregateException aggException) { var aggException = task.Exception; ... }