Task.WhenAll(taskList).Wait() 是不是与 Task.WaitAll(taskList) 相同?

Posted

技术标签:

【中文标题】Task.WhenAll(taskList).Wait() 是不是与 Task.WaitAll(taskList) 相同?【英文标题】:Is Task.WhenAll(taskList).Wait() the same as Task.WaitAll(taskList)?Task.WhenAll(taskList).Wait() 是否与 Task.WaitAll(taskList) 相同? 【发布时间】:2021-11-15 22:24:37 【问题描述】:

这些行的行为是否完全相同(包括通过 AggregateException 进行的异常处理)?

Task.WhenAll(taskList).Wait()
Task.WaitAll(taskList)

谢谢

【问题讨论】:

这能回答你的问题吗? WaitAll vs WhenAll 没有。两者都阻止,但第一个更糟,因为它没有必要。它开始异步等待只是被Wait() 阻止。根本不要阻止,使用await Task.WhenAll() 如果有的话,Task.WhenAll(taskList).Wait() 可能会导致原始异常的嵌套更深,并使用AggregateException(AggregateException(Exception[])) 而不是AggregateException(Exception[]))。使用await Task.WhenAll() 来获取第一个实际的Exception taskList 的类型是什么?是List<Task> 还是List<Task<TResult>> @MarkC。 : 不,不是真的,这篇文章让我想到了这个问题。 @PanagiotisKanavos:是的,我知道,但在我的特殊情况下,我必须使用 Wait() 并且不能使用 await。 @TheodorZoulias:在我的特殊情况下是List<Task<TResult>>,但你认为这对我的问题重要吗?感谢@all 的回答。我无法确定这些行之间的任何区别,但我会调查异常的嵌套 【参考方案1】:

让我们通过实验找出答案:

var task1 = Task.FromResult(13);
var task2 = Task.FromCanceled<int>(new CancellationToken(true));
var task3 = Task.FromCanceled<int>(new CancellationToken(true));
var task4 = Task.FromException<int>(new ApplicationException());
var task5 = Task.FromException<int>(new OverflowException());
Test("Successful+Canceled+Canceled", new[]  task1, task2, task3 );
Test("Successful+Failed+Failed", new[]  task1, task4, task5 );
Test("Successful+Canceled+Failed+Failed", new[]  task1, task2, task4, task5 );
Test("Successful+Canceled+Canceled+Failed", new[]  task1, task2, task3, task4 );

static void Test(string title, Task<int>[] tasks)

    Console.WriteLine();
    Console.WriteLine(title);
    try  Task.WaitAll(tasks); 
    catch (AggregateException ex)
    
        Console.WriteLine($"WaitAll():      ToString(ex)");
    
    try  Task.WhenAll(tasks).Wait(); 
    catch (AggregateException ex)
    
        Console.WriteLine($"WhenAll.Wait(): ToString(ex)");
    


static string ToString(AggregateException aex) 
    return $"(aex.InnerExceptions.Count) " +
        String.Join(", ", aex.InnerExceptions.Select(ex => ex.GetType().Name));

输出:

Successful+Canceled+Canceled
WaitAll():      (2) TaskCanceledException, TaskCanceledException
WhenAll.Wait(): (1) TaskCanceledException

Successful+Failed+Failed
WaitAll():      (2) ApplicationException, OverflowException
WhenAll.Wait(): (2) ApplicationException, OverflowException

Successful+Canceled+Failed+Failed
WaitAll():      (3) TaskCanceledException, ApplicationException, OverflowException
WhenAll.Wait(): (2) ApplicationException, OverflowException

Successful+Canceled+Canceled+Failed
WaitAll():      (3) TaskCanceledException, TaskCanceledException, ApplicationException
WhenAll.Wait(): (1) ApplicationException

Try it on Fiddle.

我们看到的是,Task.WaitAll() 方法按原样传播任务的异常,而 Task.WhenAll().Wait() 方法仅传播单个 TaskCanceledException,并且仅在没有其他类型的异常发生时发生了。

还应该提到的是,使用Task.WaitAll,您可以获得更多开箱即用的选项,例如millisecondsTimeout,或cancellationToken,或两者兼而有之。

【讨论】:

非常感谢。无法更好地解释!

以上是关于Task.WhenAll(taskList).Wait() 是不是与 Task.WaitAll(taskList) 相同?的主要内容,如果未能解决你的问题,请参考以下文章

异步/等待死锁 Task.WaitAll 与 Task.WhenAll [重复]

Parallel.ForEach 与 Task.Run 和 Task.WhenAll

为啥不等待 Task.WhenAll 抛出 AggregateException?

Task CancellationTokenSource和Task.WhenAll的应用

使用 Task.WhenAll 一次向我的 WebAPI 发送多个请求

从 Task.WhenAll 调用的异步方法使用 DbContext 并返回错误