我有两项任务。我用Task.WhenAll运行它们。如果其中一个抛出异常会发生什么?另一个会完成吗?
答案 0 :(得分:2)
只需运行此代码即可对其进行测试:
private async void button1_Click(object sender, EventArgs e)
{
try
{
await Task.WhenAll(DoLongThingAsyncEx1(), DoLongThingAsyncEx2());
}
catch (Exception ex)
{
Debugger.Break();
}
}
Task DoLongThingAsyncEx1()
{
return Task.Run(() =>
{
Task.Delay(1000).Wait();
throw new InvalidTimeZoneException();
});
}
Task DoLongThingAsyncEx2()
{
return Task.Run(() =>
{
Task.Delay(10000).Wait();
throw new ArgumentException();
});
}
调试器将在10秒内停止。抛出两个异常,但Debugger.Break()
仅被触发一次。更重要的是,ex
值不是AggregateException
,而是InvalidTimeZoneException
。这是因为新的async/await
将解包到实际的异常中,以帮助我们编写更直观的代码。您可以阅读更多here。如果您想阅读其他Exceptions
(不仅是第一个),则必须从Task
方法返回的WhenAll
中读取它们。
答案 1 :(得分:2)
有相同的问题,并由我自己进行了测试。简而言之:
它总是等待所有任务完成。
在所有任务完成后,如果有任何异常(如果没有捕获,则崩溃),将引发第一个异常。
对于所有例外,保留Task
返回的Task.WhenAll
实例并使用Exception.InnerExceptions
属性。
这是我的考试:
static async Task Main(string[] args)
{
var tasks = new[] { Foo1(), Foo2(), Foo3() };
Task t = null;
try
{
t = Task.WhenAll(tasks);
await t;
}
catch (Exception ex)
{
Console.WriteLine($"{ex.GetType().Name}: {ex.Message}");
}
Console.WriteLine("All have run.");
if (t.Exception != null)
{
foreach (var ex in t.Exception.InnerExceptions)
{
Console.WriteLine($"{ex.GetType().Name}: {ex.Message}");
}
}
}
static async Task Foo1()
{
await Task.Delay(50);
throw new ArgumentException("zzz");
}
static async Task Foo2()
{
await Task.Delay(1000);
Console.WriteLine("Foo 2");
throw new FieldAccessException("xxx");
}
static async Task Foo3()
{
for (int i = 0; i < 10; i++)
{
await Task.Delay(200);
Console.WriteLine("Foo 3");
}
}
输出:
Foo 3
Foo 3
Foo 3
Foo 3
Foo 2
Foo 3
Foo 3
Foo 3
Foo 3
Foo 3
Foo 3
ArgumentException: zzz
All have run.
ArgumentException: zzz
FieldAccessException: xxx
答案 2 :(得分:1)
不会因为另一个失败而停止。
Task.When
将等待所有操作完成,无论是否失败。我刚刚对此进行了测试以进行验证-完成过程花了5秒钟:
Task allTasks = Task.WhenAll(getClientToken, getVault, Task.Delay(5000));
如果要对任务进行分组,可以创建一个“新任务”,然后等待。
Task allTasks = Task.WhenAll(getClientToken, getVault, Task.Delay(5000));
try
{
await allTasks;
} catch (Exception ex)
{
// ex is the 'unwrapped' actual exception
// I'm not actually sure if it's the first task to fail, or the first in the list that failed
// Handle all if needed
Exceptions[] allExceptions = allTasks.Exceptions;
// OR
// just get the result from the task / exception
if (getVault.Status == TaskStatus.Faulted)
{
...
}
}