WaitAll vs WhenAll

Yar*_*evi 308 c# asynchronous task-parallel-library async-await

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

Jon*_*eet 467

Task.WaitAll 阻止当前线程直到一切都完成.

Task.WhenAll返回一个任务,表示等待一切都完成的动作.

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

await Task.WhenAll(tasks);
Run Code Online (Sandbox Code Playgroud)

...这意味着你的方法将在一切都完成后继续,但是你不会把一个线程绑在那里直到那个时候.

  • @CatShoes:不是 - 我已经解释过了.我想我可以给出一个类比 - 这就像订购外卖然后站在门口等待它到达之间的区别,订购外卖,做其他东西然后在快递到达时打开门...... (110认同)
  • @KevinBui:不,它不应该*阻止*它 - 它将等待*WhenAll`返回的任务,但这与阻塞线程不同. (5认同)
  • @Vince:我认为"与线程无关"是一种夸大其词,了解异步操作如何与线程交互非常重要. (4认同)
  • @JonSkeet 也许这两者之间的确切区别对我来说太微妙了。你能指点我(可能还有我们其他人)一些可以明确区别的参考资料吗? (4认同)
  • @RachitGupta:这取决于所涉及的代码 - 如果您创建任务的方式接受取消令牌并定期检查,那么取消该令牌应该取消该作业.我建议你阅读MSDN上的取消令牌. (4认同)
  • 经过大量阅读,很明显异步与线程无关 http://blog.stephencleary.com/2013/11/there-is-no-thread.html (3认同)
  • @RachitGupta:确保为每个人指定了取消令牌(假设他们正在做*可以*取消的工作)并取消每个尚未完成的任务的令牌. (3认同)

tym*_*tam 46

虽然JonSkeet的回答以一种典型的优秀方式解释了差异,但最大的实际区别是异常处理.

Task.WaitAllAggregateException当任何任务抛出时,抛出一个并且您可以检查所有抛出的异常.将awaitawait Task.WhenAll解包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.
Run Code Online (Sandbox Code Playgroud)

当执行以下程序时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.
Run Code Online (Sandbox Code Playgroud)

该程序:

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();
    }
}
Run Code Online (Sandbox Code Playgroud)

  • *最大的实际区别是异常处理.*真的吗?因为那真的不是最大的实际区别.最大的实际区别是一个是异步和非阻塞,而另一个是阻塞.这比处理异常更重要****. (10认同)
  • 感谢您指出了这一点.这个解释在我目前正在工作的场景中很有用.也许不是"最大的实际差异",但绝对是一个很好的呼唤. (4认同)
  • 这种异常行为是否与此处的文档相矛盾(https://learn.microsoft.com/en-us/dotnet/api/system.threading.tasks.task.whenall?view=netframework-4.7.2#System_Threading_Tasks_Task_WhenAll__1_System_Collections_Generic_IEnumerable_System_Threading_Tasks_Task___0___)“如果任何提供的任务在错误状态下完成,则返回的任务也将以错误状态完成,其中其异常将包含来自每个提供的任务的未包装异常集的聚合。” (2认同)
  • 我认为这是“await”的一个产物,而不是两种方法之间的区别。两者都会传播“AggregateException”,直接抛出或通过属性([`Task.Exception`](https://docs.microsoft.com/en-us/dotnet/api/system.threading.tasks.task.例外)属性)。 (2认同)

小智 18

作为差异的一个例子 - 如果你有一个任务,它会对UI线程做一些事情(例如,一个代表故事板中动画的任务),如果你Task.WaitAll()阻止了UI线程并且永远不会更新UI.如果您使用,await Task.WhenAll()则不会阻止UI线程,并且将更新UI.


i10*_*100 15

他们在做什么:

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

有什么不同:

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

在以下情况下使用 which:

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

  • @MartinRhodes但是,如果您不立即等待它,而是继续进行其他一些工作并_然后_等待它怎么办?据我了解,“WaitAll”没有这种可能性。 (2认同)