WaitAll 与 WhenAll

WaitAll vs WhenAll

提问人:Yaron Levi 提问时间:5/25/2011 最后编辑:Hakan FıstıkYaron Levi 更新时间:8/19/2022 访问量:172686

问:

异步 CTP 之间和异步 CTP 之间有什么区别? 您能提供一些示例代码来说明不同的用例吗?Task.WaitAll()Task.WhenAll()

c# 异步 async-await task-parallel-library

评论


答:

739赞 Jon Skeet 5/25/2011 #1

Task.WaitAll阻止当前线程,直到所有操作都完成。

Task.WhenAll返回一个任务,该任务表示等待直到一切完成的操作。

这意味着,在异步方法中,您可以使用:

await Task.WhenAll(tasks);

...这意味着当一切都完成后,您的方法将继续,但您不会在那之前将线程捆绑起来。

评论

5赞 Razor 1/18/2015
经过大量阅读,很明显异步与线程无关 blog.stephencleary.com/2013/11/there-is-no-thread.html
26赞 Jon Skeet 1/18/2015
@Vince:我认为“与线程无关”是夸大其词,了解异步操作如何与线程交互很重要。
11赞 Jon Skeet 9/18/2015
@KevinBui:不,它不应该阻止它 - 它将等待 返回的任务,但这与阻止线程不同。WhenAll
4赞 CatShoes 12/2/2015
@JonSkeet 也许这两者之间的确切区别对我来说太微妙了。你能指出我(可能还有我们其他人)一些可以明确区别的参考资料吗?
204赞 Jon Skeet 12/2/2015
@CatShoes:不是真的——我已经尽可能地解释了。我想我可以打个比方——这就像点外卖然后站在门口等待它到达,与点外卖,做其他事情然后在快递员到达时打开门之间的区别......
42赞 J. Long 4/13/2016 #2

举个例子,
如果你有一个任务对 UI 线程执行某些操作(例如,一个在 Storyboard 中表示动画的任务),那么 UI 线程就会被阻止,并且 UI 永远不会更新。
如果使用,则 UI 线程不会被阻止,并且 UI 将更新。
Task.WaitAll()await Task.WhenAll()

评论

0赞 X.Otano 1/19/2021
如果您设置 ConfigureAwait(false),则可以避免这种情况;在等待的任务上。不过,我不建议使用 WaitAll
129赞 tymtam 11/19/2016 #3

虽然 JonSkeet 的回答以一种典型的出色方式解释了这种差异,但还有另一个区别:异常处理

Task.WaitAll当任何任务抛出时抛出,您可以检查所有抛出的异常。in 解开 并“返回”第一个异常。AggregateExceptionawaitawait Task.WhenAllAggregateException

当下面的程序执行时,输出如下。await Task.WhenAll(taskArray)

19/11/2016 12:18:37 AM: Task 1 started
19/11/2016 12:18:37 AM: Task 3 started
19/11/2016 12:18:37 AM: Task 2 started
Caught Exception in Main at 19/11/2016 12:18:40 AM: Task 1 throwing at 19/11/2016 12:18:38 AM
Done.

当执行下面的程序时,输出如下。Task.WaitAll(taskArray)

19/11/2016 12:19:29 AM: Task 1 started
19/11/2016 12:19:29 AM: Task 2 started
19/11/2016 12:19:29 AM: Task 3 started
Caught AggregateException in Main at 19/11/2016 12:19:32 AM: Task 1 throwing at 19/11/2016 12:19:30 AM
Caught AggregateException in Main at 19/11/2016 12:19:32 AM: Task 2 throwing at 19/11/2016 12:19:31 AM
Caught AggregateException in Main at 19/11/2016 12:19:32 AM: Task 3 throwing at 19/11/2016 12:19:32 AM
Done.

程序:

class MyAmazingProgram
{
    public class CustomException : Exception
    {
        public CustomException(String message) : base(message)
        { }
    }

    static void WaitAndThrow(int id, int waitInMs)
    {
        Console.WriteLine($"{DateTime.UtcNow}: Task {id} started");

        Thread.Sleep(waitInMs);
        throw new CustomException($"Task {id} throwing at {DateTime.UtcNow}");
    }

    static void Main(string[] args)
    {
        Task.Run(async () =>
        {
            await MyAmazingMethodAsync();
        }).Wait();

    }

    static async Task MyAmazingMethodAsync()
    {
        try
        {
            Task[] taskArray = { Task.Factory.StartNew(() => WaitAndThrow(1, 1000)),
                                 Task.Factory.StartNew(() => WaitAndThrow(2, 2000)),
                                 Task.Factory.StartNew(() => WaitAndThrow(3, 3000)) };

            Task.WaitAll(taskArray);
            //await Task.WhenAll(taskArray);
            Console.WriteLine("This isn't going to happen");
        }
        catch (AggregateException ex)
        {
            foreach (var inner in ex.InnerExceptions)
            {
                Console.WriteLine($"Caught AggregateException in Main at {DateTime.UtcNow}: " + inner.Message);
            }
        }
        catch (Exception ex)
        {
            Console.WriteLine($"Caught Exception in Main at {DateTime.UtcNow}: " + ex.Message);
        }
        Console.WriteLine("Done.");
        Console.ReadLine();
    }
}

评论

13赞 Urk 4/25/2018
感谢您指出这一点。这个解释在我目前正在处理的场景中很有用。也许不是“最大的实际差异”,但绝对是一个很好的呼唤。
0赞 frostshoxx 3/13/2019
异常处理是最大的实际差异,可能更适用于与await t1; await t2; await t3;await Task.WhenAll(t1,t2,t3);
2赞 Dasith Wijes 4/4/2019
这种异常行为是否与此处的文档相矛盾(learn.microsoft.com/en-us/dotnet/api/...“如果提供的任何任务在错误状态下完成,则返回的任务也将在错误状态下完成,其中其异常将包含来自每个提供的任务的一组未包装异常的聚合。”
3赞 Theodor Zoulias 3/30/2020
我认为这是 的产物,而不是两种方法之间的区别。两者都传播 ,要么直接抛出,要么通过属性(Task.Exception 属性)抛出。awaitAggregateException
28赞 i100 8/23/2018 #4

他们做什么:

  • 在内部,两者都做同样的事情。

有什么区别:

  • WaitAll 是阻塞呼叫
  • WhenAll - not - 代码将继续执行

在以下情况下使用:

  • WaitAll when 无法在没有结果的情况下继续
  • WhenAll 当什么只是被通知,而不是被阻止

评论

2赞 Jeppe 3/31/2019
@MartinRhodes 但是,如果你不立即等待它,而是继续进行其他工作,然后等待它呢?据我了解,你没有这种可能性。WaitAll
0赞 P-L 12/24/2019
@Jeppe 你不是在做完其他工作之后,把电话改成什么样子吗?我的意思是,而不是在开始任务后立即调用它。Task.WaitAll