为什么不等待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; ... }