【问题标题】:WaitAll vs WhenAllWaitAll 与 WhenAll
【发布时间】:2011-09-01 16:16:18
【问题描述】:

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

【问题讨论】:

    标签: c# asynchronous async-await task-parallel-library


    【解决方案1】:

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

    Task.WaitAll 在任何任务抛出时抛出 AggregateException,您可以检查所有抛出的异常。 await Task.WhenAll 中的 await 解开 AggregateException 并仅“返回”第一个异常。

    当下面的程序以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();
        }
    }
    

    【讨论】:

    • 感谢您指出这一点。这个解释在我目前正在工作的场景中很有用。也许不是“最大的实际差异”,但绝对是一个很好的呼吁。
    • 异常处理是最大的实际区别可能更适用于await t1; await t2; await t3;await Task.WhenAll(t1,t2,t3);之间的比较
    • 此异常行为是否与此处的文档相矛盾 (docs.microsoft.com/en-us/dotnet/api/…) “如果提供的任何任务在故障状态下完成,则返回的任务也将在故障状态下完成,其中它的异常将包含来自每个提供的任务的一组未包装异常的聚合。"
    • 我认为这是await 的产物,这两种方法之间没有区别。两者都传播AggregateException,要么直接抛出,要么通过属性(Task.Exception 属性)。
    【解决方案2】:

    他们做什么:

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

    有什么区别:

    • WaitAll 是一个阻塞调用
    • WhenAll - 不是 - 代码将继续执行

    在什么时候使用:

    • WaitAll什么时候没有结果就无法继续
    • WhenAll 什么时候只通知,而不是阻止

    【讨论】:

    • @MartinRhodes 但是,如果您不立即等待它,而是继续一些其他工作并然后等待它,该怎么办?据我了解,WaitAll 没有这种可能性。
    • @Jeppe你做了一些其他的工作之后,你不会只是区别对Task.WaitAll的调用吗?我的意思是,不要在开始任务后立即调用它。
    【解决方案3】:

    Task.WaitAll 阻塞当前线程,直到一切都完成。

    Task.WhenAll 返回一个task,表示等待一切完成的动作。

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

    await Task.WhenAll(tasks);
    

    ...这意味着您的方法将在一切完成后继续执行,但您不会占用一个线程来闲逛直到那个时候。

    【讨论】:

    • 看了很多,很明显async跟线程无关blog.stephencleary.com/2013/11/there-is-no-thread.html
    • @Vince:我认为“与线程无关”是夸大其词,了解异步操作如何与线程交互很重要。
    • @KevinBui:不,它不应该阻止它——它会等待WhenAll返回的任务,但这不一样作为阻塞线程。
    • @JonSkeet 也许这两者之间的精确区别对我来说太微妙了。您能否向我(可能还有我们其他人)指出一些可以明确区别的参考资料?
    • @CatShoes:不是真的——我已经尽可能地解释了。我想我可以打个比方——这就像点外卖然后站在门口等待它到达与点外卖然后做其他事情然后在快递到达时打开门之间的区别......跨度>
    【解决方案4】:

    作为差异的一个例子——如果你有一个任务与 UI 线程做一些事情(例如一个代表故事板中动画的任务)如果你 Task.WaitAll() 那么 UI 线程被阻塞并且 UI 是从未更新。如果你使用await Task.WhenAll(),那么UI线程不会被阻塞,UI会被更新。

    【讨论】:

    • 如果您设置 ConfigureAwait(false);关于等待的任务。不过我不推荐使用 WaitAll
    猜你喜欢
    • 1970-01-01
    • 2014-09-27
    • 1970-01-01
    • 2014-12-21
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多