Task.WhenAll 和 Task.WhenAny 是 C# 中协调多个异步任务的核心工具,它们不执行任务,而是“观察”任务的完成状态,帮你决定下一步怎么走。
Task.WhenAll:等全部跑完再继续
当你需要所有异步操作都成功完成(比如批量获取用户数据、并行上传多个文件),就用
WhenAll。它返回一个新任务,这个任务在所有输入任务都完成后才完成;如果任一任务出错或被取消,它会立刻失败,并抛出包含所有异常的
AggregateException。 传入
IEnumerable<task></task>或
Task[],返回
Task;传入
Task<t>[]</t>,返回
Task<t></t>结果数组顺序与输入顺序严格一致,哪怕实际完成顺序不同 注意异常处理:建议用
try/catch捕获
AggregateException,再遍历
InnerExceptions分析具体失败项
Task.WhenAny:谁先回来就先处理谁
适合“抢答”场景,比如设置超时、从多个服务中取最快响应、或实现降级逻辑。它返回一个
Task<task></task>,里面包裹最先完成的那个原始任务(无论成功、失败还是取消)。 拿到结果后,要 await 那个内部 Task 才能真正读取值或捕获异常 其余未完成的任务不会自动取消,需手动调用
CancellationTokenSource.Cancel()或保留引用后续处理 常配合
Task.Delay()实现超时控制,例如:
await Task.WhenAny(apiCall, Task.Delay(3000)) == apiCall ? ... : "超时"
别混淆:它们不是并发执行器
WhenAll和
WhenAny本身不启动任务——你得先调用异步方法(如
GetDataAsync())拿到
Task对象,再把它们传进去。常见错误是写成
WhenAll(GetDataAsync(), GetDataAsync()),这其实是同步调用两次,不是并行发起。 正确做法:先创建任务列表,再统一传入
WhenAll示例:
var tasks = urls.Select(u => HttpClient.GetAsync(u)); await Task.WhenAll(tasks);若想限制并发数(比如最多同时请求 5 个),要用
SemaphoreSlim或
Parallel.ForEachAsync(.NET 6+)
选哪个?看你的业务意图
问自己一个问题:“我关心的是所有结果,还是第一个结果?”
要汇总、校验、批量提交 → 选WhenAll要快速响应、容错降级、避免等待 → 选
WhenAny两者可嵌套使用,比如用
WhenAny控制整体超时,内部用
WhenAll并行拉取子数据
基本上就这些。用对了,异步代码既清晰又健壮。
