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


当前回答

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

的任务。当任何任务抛出时,WaitAll都会抛出AggregateException,您可以检查所有抛出的异常。等待任务中的等待。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.

当使用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();
    }
}

其他回答

的任务。WaitAll阻塞当前线程,直到所有线程都完成。

的任务。WhenAll返回一个任务,该任务表示等待所有操作完成。

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

await Task.WhenAll(tasks);

... 这意味着当所有事情都完成时,您的方法将继续,但您不会绑定一个线程,只是在此之前徘徊。

举个不同的例子——如果你有一个任务对UI线程做了一些事情(例如,一个任务在Storyboard中表示动画),如果你task . waitall(),那么UI线程被阻塞,UI永远不会更新。如果你使用await Task.WhenAll(),那么UI线程不会被阻塞,并且UI将被更新。

这个任务。WaitAll阻塞当前线程。它将一直处于阻塞状态,直到所有其他任务完成执行。它有一个void返回值。这个任务。WhenAll方法返回一个Task<TResult[]>。它用于创建当且仅当所有其他任务完成时才完成的任务。

什么时候用哪个?

这是我唯一一次使用Task。WaitAll在一个非异步函数中(必须保持非异步),我想向它添加并发性。但是,要注意:这可能导致死锁,因为它阻塞了当前线程。

考虑到这一点,任何时候你都可以将函数转换为异步,这样做,并使用Task。WhenAll,带着等待。这绝对是首选的方法。

异常

的任务。当任何任务抛出时,WaitAll都会抛出AggregateException,您可以检查所有抛出的异常。等待任务中的等待。WhenAll打开AggregateException并只“返回”第一个异常。在这两种情况下,所有任务都将运行,即使其中一个抛出异常。

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

的任务。当任何任务抛出时,WaitAll都会抛出AggregateException,您可以检查所有抛出的异常。等待任务中的等待。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.

当使用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();
    }
}

他们是做什么的:

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


有什么不同:

WaitAll是阻塞调用 WhenAll - not - code将继续执行


在以下情况下使用which:

在没有结果的情况下不能继续等待 WhenAll当什么只是要通知,而不是阻止