异步 CTP 中的 Task.WaitAll()
和 Task.WhenAll()
有什么区别?您能否提供一些示例代码来说明不同的用例?
Task.WaitAll
阻塞当前线程,直到一切都完成。
Task.WhenAll
返回一个 task,表示等待一切完成的动作。
这意味着从异步方法中,您可以使用:
await Task.WhenAll(tasks);
...这意味着当一切都完成后,您的方法将继续,但您不会占用线程来闲逛直到那个时候。
虽然 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);
之间的比较
作为差异的一个示例 - 如果您有一个任务与 UI 线程(例如,代表故事板中的动画的任务)如果您 Task.WaitAll()
则 UI 线程被阻止并且 UI 永远不会更新。如果您使用 await Task.WhenAll()
,则 UI 线程不会被阻塞,并且 UI 将被更新。
他们在做什么:
在内部,两者都做同样的事情。
有什么不同:
WaitAll 是一个阻塞调用
WhenAll - not - 代码将继续执行
在以下情况下使用 which:
WaitAll 在没有结果的情况下无法继续
WhenAll 什么时候只通知什么,而不是阻塞
WaitAll
没有这种可能性。
Task.WaitAll
改变对Task.WaitAll
的调用吗?我的意思是,而不是在开始任务后立即调用它。
Task.WaitAll
阻塞当前线程。在所有其他任务完成执行之前,它将保持阻塞状态。它有一个 void 返回值。 Task.WhenAll
方法返回一个 Task<TResult[]>
。它用于创建当且仅当所有其他任务都已完成时才会完成的任务。
什么时候用哪个?
大约我唯一一次使用 Task.WaitAll
是在我想添加并发性的非 async
函数(必须保持非 async
)内。但是,请注意:这可能会导致死锁,因为它会阻塞当前线程。
考虑到这一点,您可以随时将函数转换为 async
,执行此操作,然后使用 Task.WhenAll
,其上带有 await
。这绝对是首选方法。
例外
当任何任务抛出时,Task.WaitAll
会抛出 AggregateException
,您可以检查所有抛出的异常。 await Task.WhenAll
中的 await
解开 AggregateException
并仅“返回”第一个异常。在这两种情况下,所有任务都将运行,即使其中一个引发异常。
WhenAll
返回的任务,但这与阻塞线程不同。