在这段代码中:
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
创建并抛出一个AggregateException
,因为至少有一个等待它的任务抛出异常。相反,我正在收回其中一个任务抛出的单个异常。
WhenAll
不总是创造一个AggregateException
?
我并不完全记得在哪里,但我在某处读到了新的async / await关键字,他们将AggregateException
解包到实际的异常中。
因此,在catch块中,您将获得实际的异常,而不是聚合的异常。这有助于我们编写更自然,更直观的代码。
这也是为了更容易地将现有代码转换为使用async / await所需要的,其中许多代码需要特定的异常而不是聚合的异常。
- 编辑 -
得到它了:
比尔瓦格纳说:(在例外情况下发生)
...当您使用await时,编译器生成的代码会解包AggregateException并抛出基础异常。通过利用await,您可以避免额外的工作来处理Task.Result,Task.Wait和Task类中定义的其他Wait方法使用的AggregateException类型。这是使用await而不是底层Task方法的另一个原因....
我知道这是一个已经回答的问题,但所选择的答案并没有真正解决OP的问题,所以我想我会发布这个问题。
此解决方案为您提供聚合异常(即,各种任务抛出的所有异常)并且不会阻塞(工作流仍然是异步的)。
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");
}
关键是在等待它之前保存对聚合任务的引用,然后您可以访问其Exception属性,该属性保存您的AggregateException(即使只有一个任务引发了异常)。
希望这仍然有用。我知道我今天遇到了这个问题。
您可以遍历所有任务以查看是否有多个任务抛出异常:
private async Task Example()
{
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);
}
}
private Task DoLongThingAsyncEx1()
{
return Task.Run(() => { throw new InvalidTimeZoneException(); });
}
private Task DoLongThingAsyncEx2()
{
return Task.Run(() => { throw new InvalidOperationException(); });
}
你在考虑Task.WaitAll
- 它会抛出一个AggregateException
。
WhenAll只抛出它遇到的异常列表的第一个异常。
我想我会扩展@ Richiban的答案,说你也可以通过从任务中引用它来处理catch块中的AggregateException。例如:
async Task Main()
{
var task = Task.WhenAll(A(), B());
try
{
var results = await task;
Console.WriteLine(results);
}
catch (Exception ex)
{
// This doesn't fire until both tasks
// are complete. I.e. so after 10 seconds
// as per the second delay
// The ex in this instance is the first
// exception thrown, i.e. "A".
var firstExceptionThrown = ex;
// This aggregate contains both "A" and "B".
var aggregateException = task.Exception;
}
}
public async Task<int> A()
{
await Task.Delay(100);
throw new Exception("A");
}
public async Task<int> B()
{
// Extra delay to make it clear that the await
// waits for all tasks to complete, including
// waiting for this exception.
await Task.Delay(10000);
throw new Exception("B");
}
在您的代码中,第一个异常由设计返回,如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
}
这适合我
private async Task WhenAllWithExceptions(params Task[] tasks)
{
var result = await Task.WhenAll(tasks);
if (result.IsFaulted)
{
throw result.Exception;
}
}
试试这个代码模式:
Task task = null;
try
{
task = Task.WhenAll(...);
await task;
}
catch (AggregateException aggException)
{
var aggException = task.Exception;
...
}