Task.WhenAll 等所有任务完成并聚合结果,顺序与输入一致,任一失败则整体失败;Task.WhenAny 返回最先完成的任务(Task),适用于竞速场景,其余任务不受影响。

Task.WhenAll 和 Task.WhenAny 是 C# 中用于协调多个异步任务的核心工具,它们不直接执行并行计算,而是帮你“等待多个任务的完成状态”——一个等全部,一个等最先完成的那个。
Task.WhenAll:等所有任务都完成
当你有一组独立的异步操作(比如同时调用多个 API、读取多个文件),且必须等它们全部结束才能继续时,就用 WhenAll。它返回一个 Task(如果原任务是 Task
常见写法:
- 传入 Task[] 或 IEnumerable
,例如: await Task.WhenAll(task1, task2, task3)或await Task.WhenAll(tasks) - 如果任意一个任务出错(抛异常或进入 Faulted 状态),WhenAll 返回的任务也会立刻变成 Faulted,异常信息会封装在 AggregateException 中(可遍历 InnerExceptions 查看具体哪个任务失败)
- 所有任务成功时,返回值是结果数组,顺序和传入顺序一致 —— 即使某个任务实际完成得晚,结果位置也不变
Task.WhenAny:只等第一个任务完成
适用于“谁先回来就先处理谁”的场景,比如:对同一请求发起多个冗余服务调用(如不同 CDN 节点),取最快响应;或实现带超时的等待逻辑。
关键点:
- 返回的是 Task
,即“一个代表‘最先完成的那个任务’的 Task”,需 await 两次才能拿到实际结果(第一次等 WhenAny 完成,第二次等那个被选中的任务完成) - 被选中的任务一定处于 RanToCompletion、Faulted 或 Canceled 状态;其余任务仍可能在运行中,不会被取消或中断 —— 你得自己决定是否要取消它们(比如用 CancellationToken)
- 如果有多个任务几乎同时完成,WhenAny 只返回其中任意一个(无确定顺序保证)
简单对比与选用建议
两者不是替代关系,而是解决不同问题:
- 需要聚合结果(如汇总所有接口数据)→ 用 WhenAll
- 需要快速响应或做“竞速选择”→ 用 WhenAny
- 想等所有任务但允许部分失败后继续?不能直接靠 WhenAll —— 得先把每个任务包装成不抛异常的版本(例如用
task.ContinueWith(t => t.Exception?.InnerException)或 try/catch 包一层)
一个小提醒:别混淆“并发”和“并行”
WhenAll/WhenAny 本身不开启线程或强制并行;它们只是调度和等待已启动的异步任务。真正是否并发,取决于你传进去的任务本身(比如 HttpClient.GetAsync 是 I/O 异步,不占线程;Task.Run(() => CPUWork()) 才会用线程池线程)。所以不要以为用了 WhenAll 就自动多线程了 —— 它只是让等待更高效。
基本上就这些。用对场景,再注意异常处理和任务生命周期,这两个方法就很稳。










