在异常的情况下,Task.WhenAll是否等待所有任务

时间:2018-03-21 10:09:20

标签: c# async-await task

我有两项任务。我用Task.WhenAll运行它们。如果其中一个抛出异常会发生什么?另一个会完成吗?

3 个答案:

答案 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) 
   {
       ...
   }
}