Task.WaitAll
和 Task.WhenAll
是两个不同的方法,它们都用于等待一组任务完成,但它们之间存在一些重要的区别。
Task.WaitAll
WaitAll
是一个同步方法,它会阻塞当前线程直到所有指定的任务完成。这意味着如果你在一个需要响应用户输入的线程上调用 WaitAll
,那么这个线程将会停止响应,直到所有任务完成。因此,在 GUI 应用程序中不推荐在 UI 线程上使用 WaitAll
。
使用示例
using System;
using System.Threading.Tasks;
class Program
{
static void Main()
{
var tasks = new Task[]
{
Task.Run(() => Task.Delay(1000)),
Task.Run(() => Task.Delay(500))
};
try
{
// 这里会阻塞主线程直到所有任务完成
Task.WaitAll(tasks);
Console.WriteLine("All tasks completed.");
}
catch (AggregateException ex)
{
// 处理异常
foreach (var innerEx in ex.InnerExceptions)
{
Console.WriteLine(innerEx.Message);
}
}
}
}
Task.WhenAll
WhenAll
是一个异步方法,它不会阻塞调用线程,并且返回一个新的 Task<Task[]>
,这个新任务在所有指定的任务完成后完成。你可以通过调用 Result
属性或使用 await
关键字来获取这些任务的结果或者等待它们完成。
使用示例
using System;
using System.Threading.Tasks;
class Program
{
static async Task Main()
{
var tasks = new Task[]
{
Task.Run(() => Task.Delay(1000)),
Task.Run(() => Task.Delay(500))
};
try
{
// 这里不会阻塞主线程
await Task.WhenAll(tasks);
Console.WriteLine("All tasks completed.");
}
catch (AggregateException ex)
{
// 处理异常
foreach (var innerEx in ex.InnerExceptions)
{
Console.WriteLine(innerEx.Message);
}
}
}
}
总结
-
Task.WaitAll:
- 同步方法。
- 阻塞当前线程直到所有任务完成。
- 不适用于 UI 线程。
- 适合后台线程池线程或其他不需要立即响应的场景。
-
Task.WhenAll:
- 异步方法。
- 不会阻塞当前线程。
- 返回一个任务,该任务在所有指定的任务完成后完成。
- 可以安全地在 UI 线程中使用。
注意事项
-
在使用
WaitAll
或者WhenAll
时,如果任何一个任务抛出了异常,那么这些异常会被封装到一个AggregateException
中。你需要捕获并处理这个异常来获取内部的异常信息。 -
在使用
WhenAll
时,如果你在非异步上下文中使用它(即没有使用async
和await
),你可能需要等待它的完成,例如通过调用Result
或者Wait
方法。 -
WhenAll
返回一个Task<Task[]>
类型的对象,你可以通过访问.Result
来获取已完成的任务列表。 -
如果你需要对每个任务单独进行操作,考虑使用
Task.WhenAny
或者Task.WhenAll
结合 LINQ 的Select
方法。