为何异步任务two先被取消完成,任务one却从不触发异常?
异步任务取消逻辑解析:为何任务Two先被取消且任务One无异常?
给定代码
static async Task Main(string[] args) { var cts = new CancellationTokenSource(4000); try { await DoOperationAsync(cts.Token); } catch (OperationCanceledException) { Console.WriteLine ($"Operation canceled prematurely at {DateTime.Now}"); } Console.WriteLine("Press any key..."); Console.ReadKey(true); } static async Task DoOperationAsync (CancellationToken cancellationToken) { var t1 = Method("one", 5000, cancellationToken); var t2 = Method("two", 5000, cancellationToken); var first = await Task.WhenAny(t1, t2); Console.WriteLine ($"Finished first: {(first == t2 ? "two" : "one")}, {first.Status}"); var result = await first; } static async Task<string> Method (string name, int delayMs, CancellationToken cancellationToken) { Console.WriteLine($"Started method {name} at {DateTime.Now}"); await Task.Delay(delayMs, cancellationToken); Console.WriteLine($"Finished method {name} at {DateTime.Now}"); return name; }
运行输出
Started method one at 2022-09-01 09:02:43 Started method two at 2022-09-01 09:02:43 Finished first: two, Canceled Operation canceled prematurely at 2022-09-01 09:02:47 Press any key... Finished method one at 2022-09-01 09:02:48
问题
为何任务two虽晚于任务one启动,却先被取消并完成,且任务one从不触发异常?
解答
1. 任务启动顺序的误解
输出中"Started method one"和"two"的先后只是控制台输出的微小时序差异,实际两个Method调用是同步连续执行的——异步方法在遇到第一个await前都是同步执行的,因此两个任务几乎同时启动,不存在谁晚于谁的情况。
2. 为何Task.WhenAny返回Canceled状态的t2
CancellationTokenSource(4000)会在程序启动4秒后触发取消信号,而Task.Delay(5000)需要执行5秒,因此两个任务都会收到取消信号。Task.WhenAny的核心逻辑是返回第一个进入完成状态(包括Canceled、Faulted、RanToCompletion)的任务。这里t2的Task.Delay先响应了取消信号,进入Canceled状态,所以被Task.WhenAny优先返回——这是多任务竞争中的正常时序差异,换个运行环境可能会是t1先返回。
3. 任务one无异常且完成的原因
- 当
await first(即t2)时,t2的OperationCanceledException会被抛出,被Main方法的catch块捕获,程序继续执行后续代码,但此时t1仍在后台运行。 - 由于取消信号的传递和
Task.Delay的执行存在微小的时间误差,t1的Task.Delay刚好在取消信号到达前完成了5秒的延迟,因此没有触发取消异常,顺利执行完Method方法的后续代码,输出了完成信息。 - 假设取消信号提前一点到达t1,那t1同样会抛出
OperationCanceledException,但因为我们从未await t1,这个异常会被封装在t1的Task.Exception属性中,不会主动抛出(.NET Core及以后版本会自动处理未观察的任务异常,不会导致进程崩溃),同时Method中await Task.Delay之后的代码也不会执行。
内容的提问来源于stack exchange,提问作者rory.ap
相关产品推荐
相关产品推荐

