如何知道CancellationToken是否有已注册的取消方法

本文关键字:注册 取消 方法 何知道 CancellationToken 是否 | 更新日期: 2023-09-27 17:51:19

我有一个包含CancellationTokenSource的父对象。该对象将其CancellationToken传递给与外部服务进行顺序通信的进程。每当调用外部服务时,CancellationToken被注册到一个方法,该方法将允许进程停止等待外部服务响应:

myObj.CancellationTokenRegistration.Dispose();
myObj.CancellationTokenRegistration = myObj.CancellationToken.Register(() => CancelMethod(myObj));

仅给定CancellationTokenSource,是否有一种方法可以知道针对令牌注册了取消方法?

如何知道CancellationToken是否有已注册的取消方法

如果不使用反射(或其他类型的巫术)来查看CancellationTokenSource的内部状态,就没有办法知道某些代码是否添加了任何注册。

如果你想使用反射,你应该看看这个字段:

private volatile SparselyPopulatedArray<CancellationCallbackInfo>[] m_registeredCallbacksLists;

m_callbackInfo字段似乎包含相同的信息。

        CancellationTokenSource cts = new CancellationTokenSource();
        Action test = CancelMethod;
        CancellationTokenRegistration = cts.Token.Register(test);
        var fieldInfo = typeof(CancellationTokenRegistration).GetField("m_callbackInfo", BindingFlags.NonPublic | BindingFlags.Instance);
        object fieldValue = fieldInfo.GetValue(CancellationTokenRegistration);
        var callbackFieldInfo = fieldValue.GetType().GetField("Callback", BindingFlags.Instance | BindingFlags.NonPublic);
        var callbackValue = callbackFieldInfo.GetValue(fieldValue);
        var stateForCallbackFieldInfo = fieldValue.GetType().GetField("StateForCallback", BindingFlags.Instance | BindingFlags.NonPublic);
        var stateForCallbackValue = stateForCallbackFieldInfo.GetValue(fieldValue);
        // stateForCallbackValue == CancelMethod; if Token.Register is called with one of the Action<object> arguments 
        // callbackValue == CancelMethod
        private void CancelMethod()
        {
            throw new System.NotImplementedException();
        }

下面是一些使用反射枚举已注册动作的粗略代码(在。net 4.7.1中正常工作):

public static IEnumerable<Action<object>> Registrations(this CancellationToken token)
{
        var sourceFieldInfo = typeof(CancellationToken).GetField("m_source", BindingFlags.NonPublic | BindingFlags.Instance);
        var cancellationTokenSource = (CancellationTokenSource)sourceFieldInfo.GetValue(token);
        var callbacksFieldInfo = typeof(CancellationTokenSource).GetField("m_registeredCallbacksLists", BindingFlags.NonPublic | BindingFlags.Instance);
        var callbaskLists = (Array)callbacksFieldInfo.GetValue(cancellationTokenSource);
        foreach (var sparselyPopulatedArray in callbaskLists)
        {
            if (sparselyPopulatedArray == null)
            {
                continue;
            }
            var sparselyPopulatedArrayType = sparselyPopulatedArray.GetType();
            var tailFieldInfo = sparselyPopulatedArrayType.GetProperty("Tail", BindingFlags.NonPublic | BindingFlags.Instance);
            var tail = tailFieldInfo.GetValue(sparselyPopulatedArray);
            var sparselyPopulatedArrayFragmentType = tail.GetType();
            var elementsTypeFieldInfo = sparselyPopulatedArrayFragmentType.GetField("m_elements", BindingFlags.NonPublic | BindingFlags.Instance);
            var elements = (Array)elementsTypeFieldInfo.GetValue(tail);
            foreach (var callbackInfo in elements)
            {
                if (callbackInfo == null)
                {
                    continue;
                }
                var callbackInfoType = callbackInfo.GetType();
                var callbackFieldInfo = callbackInfoType.GetField("Callback", BindingFlags.NonPublic | BindingFlags.Instance);
                var callback = (Action<object>)callbackFieldInfo.GetValue(callbackInfo);
                if (callback != null)
                {
                    yield return callback;
                }
            }
        }
    }