Task.WhenAll
是 .NET 中用于并行等待多个异步任务的核心方法。它可以让多个异步操作同时执行,然后一次性等待所有任务完成,而不是逐个等待。
async void Main()
{
// 准备数据:[1,2,3,4,5,6,7,8,9,10]
var inputs = Enumerable.Range(1,10).ToArray();
var outputs = new List<int>();
// ❌ 错误方式:串行执行(一个接一个)
//foreach (var element in inputs)
//{
// var result = await HeavyJobAsync(element); // 等待当前任务完成
// outputs.Add(result); // 再开始下一个
//}
// 总耗时:10 × 200ms = 2000ms
// ✅ 正确方式:使用 Task.WhenAll 并行执行
var tasks = new List<Task<int>>();
// 第一步:启动所有任务(不等待结果)
foreach (var element in inputs)
{
// 关键:这里创建并启动任务,但不 await,任务立即开始在后台执行
tasks.Add(HeavyJobAsync(element));
}
// 第二步:使用 Task.WhenAll 等待所有任务完成
await Task.WhenAll(tasks); // 🎯 重点!
// 第三步:收集所有任务的结果
outputs = tasks.Select(t => t.Result).ToList();
outputs.Dump(); // 输出:[1,4,9,16,25,36,49,64,81,100]
}
// 模拟耗时异步操作
async Task<int> HeavyJobAsync(int input)
{
await Task.Delay(200); // 模拟200ms的工作
return input * input; // 返回平方值
}
分析
方式1的问题:
foreach (var element in inputs)
{
var result = await HeavyJobAsync(element); //这里会"阻塞"
// 程序会在这里停下来等待,直到HeavyJobAsync完成
// 然后才会执行下一次循环
}
方式2的优势:
// 第一步:快速启动所有任务
foreach (var element in inputs)
{
tasks.Add(HeavyJobAsync(element)); // 只是启动,不等待
// HeavyJobAsync立即返回Task对象,实际工作在后台进行
}
// 第二步:统一等待所有任务完成
await Task.WhenAll(tasks); // 一次性等待所有任务
Task.WhenAll 的不同用法
1. 有返回值的任务
// 启动多个任务
var task1 = GetDataAsync(1);
var task2 = GetDataAsync(2);
var task3 = GetDataAsync(3);
// 等待所有任务并获取结果
int[] results = await Task.WhenAll(task1, task2, task3);
// results = [result1, result2, result3] 按顺序排列
2. 无返回值的任务
// 启动多个任务
var task1 = SaveDataAsync(data1);
var task2 = SaveDataAsync(data2);
var task3 = SaveDataAsync(data3);
// 等待所有任务完成
await Task.WhenAll(task1, task2, task3);
3. 动态任务集合
// 动态创建任务列表
var tasks = urls.Select(url => DownloadAsync(url)).ToList();
// 等待所有下载完成
string[] contents = await Task.WhenAll(tasks);
使用场景
同时调用多个Web服务、同时读写多个文件、数据库操作:并行执行多个查询。
Task.WhenAny vs Task.WhenAll 核心区别
特性 | Task.WhenAll | Task.WhenAny |
---|---|---|
等待策略 | 等待所有任务完成 | 等待任何一个任务完成 |
完成条件 | 最慢的任务完成时 | 最快的任务完成时 |
返回结果 | 所有任务的结果数组 | 第一个完成的任务对象 |
用途 | 并行处理,需要所有结果 | 竞争场景,只要第一个结果 |