已注册的CancellationToken回调调用顺序

时间:2019-01-12 09:50:22

标签: c# async-await

如果我在取消CancellationToken之前注册了一些回调,则似乎在取消令牌时会以相反的顺序调用它们。这样的调用顺序可以保证吗?

var cts = new CancellationTokenSource();
var token = cts.Token;
token.Register(() => Console.WriteLine("1"));
token.Register(() => Console.WriteLine("2"));
token.Register(() => Console.WriteLine("3"));
cts.Cancel();

这将输出

3
2
1

1 个答案:

答案 0 :(得分:4)

好吧,在CancellationToken类的source code中,情况似乎是如此。方法ExecuteCallbackHandlers(bool throwOnFirstException)负责获取并执行回调。它包含以下代码片段:

try
{
   for (int index = 0; index < callbackLists.Length; index++)
   {
      SparselyPopulatedArray<CancellationCallbackInfo> list = Volatile.Read<SparselyPopulatedArray<CancellationCallbackInfo>>(ref callbackLists[index]);
      if (list != null)
      {
         SparselyPopulatedArrayFragment<CancellationCallbackInfo> currArrayFragment = list.Tail;

         while (currArrayFragment != null)
         {
             for (int i = currArrayFragment.Length - 1; i >= 0; i--)
             {
                ... some other code
             }
        }
     }
   }
}

在内部for循环中,它从最后一个元素向后一个元素遍历回调数组的片段。

但是,如@Nick所述,不能在文档中保证。一个简单的解决方案是将回调添加到一个委托中-这样,我们就可以更好地控制执行顺序:

token.Register(() => 
{ 
   Console.WriteLine("1"));
   Console.WriteLine("2"));
   Console.WriteLine("3"));
});