发布于2026-07-23 阅读(0)
扫一扫,手机访问
Task.WhenAll 和 Task.WhenAny 不是执行器,而是“完成信号的组合器”。
它们:
并发发生在 Task 被“创建/启动”的那一刻,而不是 WhenAll/WhenAny。
T1 ─┐T2 ─┼─▶ [ All Completed ] ─▶ Completed TaskT3 ─┘
语义:
T1 ─┐T2 ─┼─▶ [ First Completed ] ─▶ Completed TaskT3 ─┘
语义:
对每个 Task 注册 continuation
使用一个 原子计数器(remaining)
每完成一个 Task:
Interlocked.Decrement(remaining)当 remaining == 0:
? 没有线程等待,完全事件驱动
对每个 Task 注册 continuation
第一个完成的 Task:
TrySetResult(task)后续完成者:
? 只有一个能“赢”,没有计数器
WhenAny 返回的是“胜者句柄”,不是结果
| 维度 | WhenAll | WhenAny |
|---|---|---|
| 等待策略 | 全部 | 第一个 |
| 返回类型 | Task / Task | Task |
| 是否阻塞线程 | ❌ | ❌ |
| continuation 数量 | N | N |
| 内部同步 | 原子计数 | CAS / TrySet |
| 并发控制 | ❌ | ❌ |
二者都只做“信号组合”,不做“任务调度”
所有 Task 都会执行到结束
如果有异常:
内部保存 AggregateException
await WhenAll 时:
try{ await Task.WhenAll(tasks);}catch{ var all = tasks .Where(t => t.IsFaulted) .SelectMany(t => t.Exception!.InnerExceptions);}
如果“第一个完成的 Task”失败:
await completedTask 会直接抛异常其他 Task 的异常:
? WhenAny + 异常 = 高风险组合
如果任一 Task 被取消:
但:
var winner = await Task.WhenAny(tasks);cts.Cancel(); // 你自己的责任
? 几乎可以忽略
var t1 = GetUserAsync();var t2 = GetOrdersAsync();await Task.WhenAll(t1, t2);return new{ User = await t1, Orders = await t2};
适用场景
超时模式
var work = DoWorkAsync();var timeout = Task.Delay(2000);if (await Task.WhenAny(work, timeout) == timeout) throw new TimeoutException();await work;
主备切换
var tasks = new[]{ CallPrimaryAsync(), CallSecondaryAsync()};var winner = await Task.WhenAny(tasks);return await winner;
⚠️ 记得取消失败者
❌ 把 WhenAll 当“并行器”
❌ 在循环里直接 await(伪并发)
❌ WhenAny 后忽略未完成 Task
❌ WhenAll + Task.Run(Web)
❌ 忽略异常和取消
并发 = Task 创建时机
组合 = WhenAll / WhenAny
调度 = ThreadPool / Parallel
WhenAll / WhenAny 决定“怎么等”,
而不是“怎么跑”。
Task.WhenAll 是一个原子计数器驱动的完成门闩
Task.WhenAny 是一个 CAS 驱动的竞速门闩
它们本身几乎“没有重量”,
但决定了整个 async 架构的形状。
售后无忧
立即购买>office旗舰店
售后无忧
立即购买>office旗舰店
售后无忧
立即购买>office旗舰店
售后无忧
立即购买>office旗舰店
正版软件
正版软件
正版软件
正版软件
正版软件
1
2
3
7
8