异步进阶:C#的Task.WhenAll——如何开启多个异步任务
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 |
---|---|---|
等待策略 | 等待所有任务完成 | 等待任何一个任务完成 |
完成条件 | 最慢的任务完成时 | 最快的任务完成时 |
返回结果 | 所有任务的结果数组 | 第一个完成的任务对象 |
用途 | 并行处理,需要所有结果 | 竞争场景,只要第一个结果 |