为何Task.WhenAny中总是最后创建的任务率先完成?
为何取消令牌触发时,最后创建的等待任务总是最先完成?
不管怎么调整Task.WhenAny方法里signal1Task、signal2Task、signal3Task的参数顺序,最终完成的任务始终是signal3Task。但如果改变这三个任务的创建顺序,结果就会跟着变:
- 交换第1行和第3行代码,无论
Task.WhenAny的参数顺序如何,完成的任务都是signal1Task; - 交换第2行和第3行代码,完成的任务则始终是
signal2Task。
这只是巧合吗?
static async Task Main() { CancellationTokenSource cancelTokenSource = new(); AsyncManualResetEvent signal1 = new(); AsyncAutoResetEvent signal2 = new(); AsyncAutoResetEvent signal3 = new(); // 1. var signal1Task = signal1.WaitAsync(cancelTokenSource.Token); // 2. var signal2Task = signal2.WaitAsync(cancelTokenSource.Token); // 3. var signal3Task = signal3.WaitAsync(cancelTokenSource.Token); Task.Run(async () => { await Task.Delay(1000); cancelTokenSource.Cancel(); }); var completedTask = await Task.WhenAny(signal1Task, signal2Task, signal3Task); if (completedTask == signal1Task) Console.WriteLine("signal1Task"); if (completedTask == signal2Task) Console.WriteLine("signal2Task"); if (completedTask == signal3Task) Console.WriteLine("signal3Task"); }
这绝对不是巧合,核心原因在于CancellationToken触发取消时,回调的执行顺序是「后进先出」(LIFO):
当你调用WaitAsync(cancellationToken)时,方法内部会向传入的CancellationToken注册一个取消回调。当Cancel()被调用时,CancellationToken会按照回调注册的逆序来执行——也就是最后注册的回调会被最先触发。
在你的代码里:
signal1Task第一个注册取消回调;signal2Task第二个注册;signal3Task最后一个注册。
所以当1秒后Cancel()执行时,signal3Task的回调会被最先调用,它对应的任务也就最先完成,Task.WhenAny自然会返回这个任务,和你在Task.WhenAny里传入的参数顺序完全无关。
同理,如果你调整任务的创建顺序,最后创建的那个任务的回调会被最先触发,它就会成为Task.WhenAny返回的第一个完成任务。
内容的提问来源于stack exchange,提问作者theateist
相关产品推荐
相关产品推荐

