电商网站建设实训要求,网站建设策划 流程,网站怎么设置为可信任网站,如何做一个更新网站前言在上篇文章浅谈C#取消令牌CancellationTokenSource[1]一文中我们讲解了CancellationTokenSource#xff0c;它的主要功能就是分发一个令牌#xff0c;当我取消令牌我可以进行一些回调操作或者通过令牌状态得知被取消。在上文的结尾处我们也提到了#xff0c;默认情况下C… 前言 在上篇文章浅谈C#取消令牌CancellationTokenSource[1]一文中我们讲解了CancellationTokenSource它的主要功能就是分发一个令牌当我取消令牌我可以进行一些回调操作或者通过令牌状态得知被取消。在上文的结尾处我们也提到了默认情况下CancellationTokenSource产生的Token是一次性的Cancel操作之后就没办法再复用了只能释放掉了。而微软也很贴心的为我们提供了一个解决方案来解决这问题那就是我们今天要说的更改令牌ChangeToken它看起来可以让CancellationTokenSource产生的Token多次触发本文我们就来讲解ChangeToken相关。简单实例 要想更好的了解一个新的知识首先知道它是做啥的其次要知道它怎么做。所以还是老规矩咱们先通过简单的示例开始这样更方便了解。ChangeToken本身是一个静态类它的核心入口OnChange方法包含两个参数一个是传递IChangeToken接口实例来获取令牌另一个是令牌取消之后进行的回调操作。博主本人知道的关于IChangeToken接口的在CLR中的实现类有两个分别是CancellationChangeToken和CompositeChangeToken类接下来咱们就分别介绍一下这两个类的简单使用。CancellationChangeToken示例咱们先来演示CancellationChangeToken类的使用方式这也是默认情况下可以使用ChangeToken的最简单方式。首先定义一个TestCancellationChangeToken类来包装一下CancellationChangeToken实现如下public class TestCancellationChangeToken
{private CancellationTokenSource tokenSource;/// summary/// 获取CancellationChangeToken实例方法/// /summarypublic CancellationChangeToken CreatChanageToken(){tokenSource new CancellationTokenSource();return new CancellationChangeToken(tokenSource.Token);}/// summary/// 取消CancellationTokenSource/// /summarypublic void CancelToken(){tokenSource.Cancel();}
}
这个类非常简单,包含一个CancellationTokenSource类型的属性一个创建CancellationChangeToken实例的方法和一个取消CancellationTokenSource的CancelToken方法。注意看实现的CreatChanageToken方法这个方法每次调用都需要创建一个新的CancellationTokenSource和CancellationChangeToken实例创建CancellationChangeToken实例需要传递CancellationToken实例。CancelToken方法里是调用的CancellationTokenSource的Cancel方法。接下来我们就来看一下如何使用定义的这个类//声明类的实例
TestCancellationChangeToken cancellationChangeToken new TestCancellationChangeToken();
ChangeToken.OnChange(() cancellationChangeToken.CreatChanageToken(), ()
{System.Console.WriteLine(${DateTime.Now:HH:mm:ss}被触发可一次);
});//模拟多次调用CancelToken
for (int i 0; i 3; i)
{Thread.Sleep(1000);cancellationChangeToken.CancelToken();
}
上面的示例演示了通过ChangeToken类使用我们定义的TestCancellationChangeToken类ChangeToken的OnChange方法传递了创建新CancellationChangeToken实例的方法委托第二个参数则是取消令牌的回调操作这样便可以重复的使用取消操作为了演示效果在循环里重复调用CancelToken方法显示的打印结果是16:40:15被触发可一次
16:40:16被触发可一次
16:40:17被触发可一次
CancellationChangeToken类是通过ChangeToken实现重复取消触发调用的简单实现两者将结合的时候需要自己包装一下因为ChangeToken的第一个参数需要每次获取CancellationChangeToken实例的委托所以需要将它包装到工作类中。CompositeChangeToken示例实际开发中很多时候都需要一些关联的场景比如我触发了一个取消操作我想把和这个相关联的其它操作也取消也就是咱们说的有相关性。CompositeChangeToken正是可以绑定一个相关性的IChangeToken集合当这个IChangeToken集合中有任何一个实例进行取消操作的时候当前CompositeChangeToken实例也会执行取消操作咱们就大致演示一下它的使用方式首先是定义一个使用类TestCompositeChangeToken来模拟包装CompositeChangeToken实例public class TestCompositeChangeToken
{//声明一个CancellationTokenSource集合private ListCancellationTokenSource _cancellationTokenSources;/// summary/// 获取CompositeChangeToken实例方法/// /summarypublic CompositeChangeToken CreatChanageToken(){//初始化三个CancellationTokenSource实例var firstCancellationTokenSource new CancellationTokenSource();var secondCancellationTokenSource new CancellationTokenSource();var threeCancellationTokenSource new CancellationTokenSource();//分别注册一个回调操作用于演示firstCancellationTokenSource.Token.Register(() System.Console.WriteLine(firstCancellationTokenSource被取消));secondCancellationTokenSource.Token.Register(() System.Console.WriteLine(secondCancellationTokenSource被取消));threeCancellationTokenSource.Token.Register(() System.Console.WriteLine(threeCancellationTokenSource被取消));//加入到集合还_cancellationTokenSources new ListCancellationTokenSource{firstCancellationTokenSource,secondCancellationTokenSource,threeCancellationTokenSource};//生成CancellationChangeToken集合var cancellationChangeTokens _cancellationTokenSources.Select(i new CancellationChangeToken(i.Token)).ToList();//传递给CompositeChangeTokenvar compositeChangeToken new CompositeChangeToken(cancellationChangeTokens);//给CompositeChangeToken实例注册一个取消回调方便演示compositeChangeToken.RegisterChangeCallback(state System.Console.WriteLine(compositeChangeToken被取消),null);return compositeChangeToken;}/// summary/// 取消CancellationTokenSource/// /summarypublic void CancelToken(){//方便演示效果在_cancellationTokenSources集合随便获取一个取消_cancellationTokenSources[new Random().Next(_cancellationTokenSources.Count)].Cancel();}
}
这里我定义了一个类在获取CompositeChangeToken实例的CreatChanageToken方法中创建了三个CancellationTokenSource实例然后用这三个实例初始化了一个CancellationChangeToken集合用这个集合初始化了一个CompositeChangeToken实例来模拟集合中的CancellationChangeToken实例和CompositeChangeToken实例的相关性。CancelToken方法中随机获取了一个CancellationTokenSource实例进行取消操作来更好的演示相关性。因为CompositeChangeToken类也实现了IChangeToken接口所以用起来都一样大致如下//声明类的实例
TestCompositeChangeToken compositeChangeToken new TestCompositeChangeToken();
ChangeToken.OnChange(() compositeChangeToken.CreatChanageToken(), ()
{System.Console.WriteLine(${DateTime.Now:HH:mm:ss}被触发可一次);
});//模拟多次调用CancelToken
for (int i 0; i 3; i)
{Thread.Sleep(1000);compositeChangeToken.CancelToken();
}
为了演示可以重复触发取消操作这里依然使用循环的方式模拟多次触发。因为存在相关性所以打印的执行结果如下12:05:18被触发可一次
compositeChangeToken被取消
secondCancellationTokenSource被取消12:05:19被触发可一次
compositeChangeToken被取消
firstCancellationTokenSource被取消12:05:20被触发可一次
compositeChangeToken被取消
secondCancellationTokenSource被取消
从结果上可以看到任何一个相关联CancellationChangeToken实例的CancellationTokenSource实例被取消的话与其相关的CompositeChangeToken实例也执行了取消操作在有些场景下还是比较实用的。源码探究上面我们通过简单的示例大致了解了ChangeToken是做啥的以及它怎么使用。通过名字可以得知它叫更改令牌说明可以动态产生令牌的值。它涉及到了几个核心的操作相关分别是IChangeToken接口、CancellationChangeToken、CompositeChangeToken和ChangeToken静态类通过上面咱们的示例和讲解我们大致了解了这几个类型的关系为了方便阅读和思维带入咱们就按照方便理解的顺序来挨个讲解。友情提示:本文设计到粘贴出来的相关源码这些源码是省略掉一部分过程的。因为我们主要是了解它的实现无关紧要的代码可能会影响阅读效果。而且这次的GitHub源码地址我更换为https://hub.fastgit.org而没有使用官方的https://github.com主要是GitHub近期很不稳定经常打不开。fastgit是github的镜像网站展示的内容是完全一致的最主要的是打开很流畅。IChangeToken接口首先便是IChangeToken接口它是整个ChangeToken系列的入口操作ChangeToken的OnChange操作也是通过它的实现类发起的。它的作用就是获取一个可以更改的令牌也就是可以重复触发的令牌咱们就先来看一下它的实现[点击查看源码????[2]]public interface IChangeToken
{/// summary/// 用来标识是否发生过更改/// /summarybool HasChanged { get; }/// summary/// 指示令牌是否支持回调/// /summarybool ActiveChangeCallbacks { get; }/// summary/// 当令牌取消时执行的回调/// /summary/// param namecallback回调执行委托/param/// param namestate回调委托的参数/param/// returnsAn see crefIDisposable/ that is used to unregister the callback./returnsIDisposable RegisterChangeCallback(Actionobject callback, object state);
}
它定义的接口成员非常简单总结起来就是两类一个是判断是否发生过更改相关即取消操作一个是发生过更改之后的回调操作。它只是定义一个标准任何实现这个标准的类都具备被ChangeToken的OnChange方法提供可更换令牌的能力。CancellationChangeToken实现上面我们了解了IChageToken接口定义的成员相关而CancellationChangeToken则是IChageToken接口最常使用默认的实现了解了它的实现我们就可以更好的知道ChangeToken的OnChange方法是如何工作的所以这里我选择了先讲解CancellationChangeToken相关的实现这样会让接下来的阅读变得更容易理解。好了直接看它的实现[点击查看源码????[3]]public class CancellationChangeToken : IChangeToken
{/// summary/// 唯一构造函数通过CancellationChangeToken初始化/// /summarypublic CancellationChangeToken(CancellationToken cancellationToken){Token cancellationToken;}/// summary/// 因为它是通过CancellationToken实现具备回调的能力/// /summarypublic bool ActiveChangeCallbacks { get; private set; } true;/// summary/// 根据CancellationToken的IsCancellationRequested属性判断令牌是否已取消/// /summarypublic bool HasChanged Token.IsCancellationRequested;/// summary/// 接收传递进来的CancellationToken/// /summaryprivate CancellationToken Token { get; }/// summary/// 注册回调操作/// /summary/// returns/returnspublic IDisposable RegisterChangeCallback(Actionobject callback, object state){try{//本质还是通过CancellationToken完成它回调操作的功能return Token.UnsafeRegister(callback, state);}catch (ObjectDisposedException){ActiveChangeCallbacks false;}return NullDisposable.Instance;}private class NullDisposable : IDisposable{public static readonly NullDisposable Instance new NullDisposable();public void Dispose(){}}
}
通过上面的代码我们可以得知CancellationChangeToken的本质还是CancellationToken的包装类因为我们看到了CancellationChangeToken类的核心操作实现都是依赖的CancellationChangeToken类的实现完成的。它的HasChanged属性和RegisterChangeCallback方法都是直接调用的CancellationChangeToken类的实现。ChangeToken类的实现上面我们讲解了IChangeToken接口的相关实现也说明了因为ChangeToken类是依赖IChangeToken接口实现来完成的所以咱们是从IChangeToken类开始讲解的。了解了上面的实现之后咱们就可以直接来看ChangeToken相关的实现了,而我们使用的就是它的OnChange方法[点击查看源码????[4]]public static IDisposable OnChange(FuncIChangeToken changeTokenProducer, Action changeTokenConsumer)
{return new ChangeTokenRegistrationAction(changeTokenProducer, callback callback(), changeTokenConsumer);
}public static IDisposable OnChangeTState(FuncIChangeToken changeTokenProducer, ActionTState changeTokenConsumer, TState state)
{return new ChangeTokenRegistrationTState(changeTokenProducer, changeTokenConsumer, state);
}
它的OnChange方法其实是包含两个重载的一个是无参委托一个是有参委托。无参委托没啥好说的通过有参回调我们可以给回调传递参数这个参数是方法传递每次回调委托获取的值取决于State本身的值是什么。最重要的是它们两个都是返回了ChangeTokenRegistrationT类的实例也就是说OnChange方法本身是一个外观用来隐藏ChangeTokenRegistration这个类的具体信息因为ChangeTokenRegistration只需要在ChangeToken内部使用。那我们就直接看一下ChangeTokenRegistration内部类的实现[点击查看源码????[5]]private class ChangeTokenRegistrationTState : IDisposable
{//生产IChangeToken实例的委托private readonly FuncIChangeToken _changeTokenProducer;//回调委托private readonly ActionTState _changeTokenConsumer;//回调参数private readonly TState _state;public ChangeTokenRegistration(FuncIChangeToken changeTokenProducer, ActionTState changeTokenConsumer, TState state){_changeTokenProducer changeTokenProducer;_changeTokenConsumer changeTokenConsumer;_state state;//执行changeTokenProducer得到IChangeToken实例IChangeToken token changeTokenProducer();//调用RegisterChangeTokenCallback方法传递IChangeToken实例RegisterChangeTokenCallback(token);}
}
通过上面我们了解到ChangeTokenRegistration正是实现ChangeToken效果的核心而它的构造函数里通过执行传递进来产生IChangeToken新实例的委托得到了新的IChangeToken实例。这里需要注意每次执行FuncIChangeToken都会得到新的IChangeToken实例。然后调用了RegisterChangeTokenCallback方法而这个方法只需要传递得到的IChangeToken实例即可。接下来我们只需要看RegisterChangeTokenCallback方法实现即可[点击查看源码????[6]]private void RegisterChangeTokenCallback(IChangeToken token)
{//给IChangeToken实例注册回调操作//回调操作正是执行当前ChangeTokenRegistration实例的OnChangeTokenFired方法IDisposable registraton token.RegisterChangeCallback(s ((ChangeTokenRegistrationTState)s).OnChangeTokenFired(), this);SetDisposable(registraton);
}
从这里我们可以看出ChangeTokenRegistration的RegisterChangeTokenCallback方法本质还是使用了IChangeToken实例的RegisterChangeCallback方法来实现的不过这里的回调执行的是当前ChangeTokenRegistration实例的OnChangeTokenFired方法也就是说令牌取消的时候调用的就是OnChangeTokenFired方法咱们直接看一下这个方法的实现[点击查看源码????[7]]private void OnChangeTokenFired()
{//获取一个新的IChangeToken实例IChangeToken token _changeTokenProducer();try{//执行注册的回调操作_changeTokenConsumer(_state);}finally{//又调用了RegisterChangeTokenCallback注册当前IChangeToken实例RegisterChangeTokenCallback(token);}
}
看上面的代码我第一反应就是豁然开朗通过OnChangeTokenFired方法的实现仿佛一切都透彻了。首先调用_changeTokenProducer委托获取新的IChangeToken实例即我们通过即我们通过ChangeToken的OnChange方法第一个参数传递的委托。然后执行_changeTokenConsumer委托即我们通过ChangeToken的OnChange方法第二个参数传递的委托。最后传递当前通过_changeTokenProducer委托产生的新IChangeToken实例调用RegisterChangeTokenCallback方法即咱们上面的那个方法这样就完成了类似一个递归的操作。执行完之后又将这套流程重新注册了一遍然后形成了这种可以持续触发的操作。上面的RegisterChangeTokenCallback方法里里调用了SetDisposable方法这个方法主要是判断Token有没有被取消。因为我们在使用IChangeToken实例的时候会涉及到多线程共享的问题而IChangeToken实例本身设计考虑到了线程安全问题我们可以大致看下SetDisposable的实现[点击查看源码????[8]]private IDisposable _disposable;
private static readonly NoopDisposable _disposedSentinel new NoopDisposable();
private void SetDisposable(IDisposable disposable)
{//读取_disposable实例IDisposable current Volatile.Read(ref _disposable);//如果当前_disposable实例等于_disposedSentinel实例则说明当前ChangeTokenRegistration已被释放//则直接释放IChangeToken实例然后返回if (current _disposedSentinel){disposable.Dispose();return;}//线程安全交换如果之前_disposable的值等于_disposedSentinel说明被释放过了//则释放IChangeToken实例IDisposable previous Interlocked.CompareExchange(ref _disposable, disposable, current);if (previous _disposedSentinel){disposable.Dispose();}//说明没有被释放过else if (previous current){}//说明别的线程操作了dispose则直接异常else{throw new InvalidOperationException(Somebody else set the _disposable field);}
}
因为ChangeTokenRegistration是实现了IDisposable接口所以我们可以先看下Dispose方法的实现这样的话会让大家更好的理解它的这个释放体系[点击查看源码????[9]]public void Dispose()
{//因为_disposable初始值是null所以把NoopDisposable实例赋值给_disposable并调用Dispose方法Interlocked.Exchange(ref _disposable, _disposedSentinel).Dispose();
}
因为初始声明_disposable变量的时候初始值是null,这里把NoopDisposable实例赋值给_disposable并调用Dispose方法。其实Dispose方法啥也没做就是为了标记一下因为ChangeTokenRegistration类并未涉及到非托管资源相关的操作。通过SetDisposable方法结合Dispose方法我们可以理解在触回调操作的时候会调SetDisposable方法进行判断ChangeTokenRegistration有没有被释放过如果已经被释放则直接释放掉传递的IChangToken实例。因为ChangeToken的OnChange方法返回的就是ChangeTokenRegistration实例如果这个被释放则意味了OnChange传递的IChangeToken实例也必须要释放。通过上面讲解了ChangeTokenRegistrationTState类的实现我们了解到了ChangeToken类工作的本质其实非常简单为了怕大家没看明白在这里咱们简单的总结一下ChangeToken的整体工作过程。•ChangeToken静态类只包装了OnChange方法这个方法传递的核心参数是产生IChangeToken实例的委托和CancellationTokenSource实例取消后的回调操作。这里的IChangeToken实例和ChangeToken静态类没啥关系就是名字长得像。•ChangeToken静态类的OnChange方法本质是包装一个ChangeTokenRegistrationTState实例。ChangeTokenRegistration是ChangeToken类工作的核心它的工作方式是初始化的时候生成IChangeToken实例然后调用RegisterChangeTokenCallback方法在RegisterChangeTokenCallback方法方法中给IChangeToken实例的RegisterChangeCallback方法注册了回调操作。•RegisterChangeCallback回调操作注册一个调用OnChangeTokenFired方法的操作通过上面的源码我们知道RegisterChangeCallback本质是给CancellationToken注册回调所以当CancellationTokenSource调用Cancel的时候回执行OnChangeTokenFired方法。•OnChangeTokenFired方法是核心操作它首先是获取一个新的IChangeToken实例然后执行注册的回调操作。然后又调用了RegisterChangeTokenCallback传递了最新获取的IChangeToken实例这样的话就形成了一个类似递归的操作而这个递归的终止条件就是ChangeTokenRegistration有没有被释放。所以才能实现动态更改令牌的效果。一句话总结一下就是RegisterChangeCallback中给CancellationChangeToken的回调注册了调用OnChangeTokenFired方法的操作OnChangeTokenFired方法中有调用了RegisterChangeCallback方法给它传递了生成的IChangeToken实例而回调操作都是同一个只是不断被新的IChangeToken实例调用。CompositeChangeToken实现上面我们说过之所以最后来说CompositeChangeToken的实现完全是因为它属于增强的操作。如果大家理解了简单的工作方式的流程然后再去尝试了解复杂的操作可能会更容易理解。所以咱们先说了CancellationChangeToken这个IChangeToken最简单的实现然后说了ChangeToken静态类让大家对整体的工作机制有了解最后咱们再来讲解CompositeChangeToken这样的话大家会很容易就理解这个操作方式的。咱们还是先从入口的构造函数入手吧[点击查看源码????[10]]public class CompositeChangeToken : IChangeToken
{public IReadOnlyListIChangeToken ChangeTokens { get; }public bool ActiveChangeCallbacks { get; }public CompositeChangeToken(IReadOnlyListIChangeToken changeTokens){ChangeTokens changeTokens ?? throw new ArgumentNullException(nameof(changeTokens));//遍历传入的IChangeToken集合for (int i 0; i ChangeTokens.Count; i){/*** 如果集合中存在任何一个IChangeToken实例ActiveChangeCallbacks为true* 则CompositeChangeToken的ActiveChangeCallbacks也为true* 因为CompositeChangeToken可以关联IChangeToken集合中的任何一个有效实例*/if (ChangeTokens[i].ActiveChangeCallbacks){ActiveChangeCallbacks true;break;}}}
}
从上面的构造函数可以看出IChangeToken集合中存在可用的实例即可因为CompositeChangeToken只需要知道集合中存在可用的即可而不是要求全部的IChangeToken都可以用。通过ChangeToken静态类的源码我们可以知道CancellationTokenSource的Cancel方法执行后调用的是IChangeToken的RegisterChangeCallback方法也就是说回调触发的操作就是这个方法我们来看一下这个方法的实现[点击查看源码????[11]]private CancellationTokenSource _cancellationTokenSource;
public IDisposable RegisterChangeCallback(Actionobject callback, object state)
{//核心方法EnsureCallbacksInitialized();//这里的CancellationTokenSource注册CompositeChangeToken的回调操作return _cancellationTokenSource.Token.Register(callback, state);
}private static readonly Actionobject _onChangeDelegate OnChange;
private bool _registeredCallbackProxy;
private ListIDisposable _disposables;
private readonly object _callbackLock new object();
private void EnsureCallbacksInitialized()
{//判断是否已使用RegisterChangeCallback注册过回调操作如果不是第一次则直接返回if (_registeredCallbackProxy){return;}//加锁 意味着这个操作要线程安全lock (_callbackLock){if (_registeredCallbackProxy){return;}//实例化CancellationTokenSource因为RegisterChangeCallback方法里再用_cancellationTokenSource new CancellationTokenSource();_disposables new ListIDisposable();//循环要关联的IChangeToken集合for (int i 0; i ChangeTokens.Count; i){//判断注册进来的IChangeToken实例是否支持回调操作if (ChangeTokens[i].ActiveChangeCallbacks){//给IChangeToken实例注册回调操作执行_onChangeDelegate委托IDisposable disposable ChangeTokens[i].RegisterChangeCallback(_onChangeDelegate, this);//返回值加入IDisposable集合_disposables.Add(disposable);}}//标识注册过了防止重复注册引发的多次触发_registeredCallbackProxy true;}
}
上面的代码我们看到了核心的关联回调操作是执行了_onChangeDelegate委托它是被OnChange方法初始化的。这一步操作其实就是把关联的IChangeToken实例注册_onChangeDelegate委托操作咱们来看下CompositeChangeToken的OnChange方法实现[点击查看源码????[12]]private static void OnChange(object state)
{//获取传递的CompositeChangeToken实例var compositeChangeTokenState (CompositeChangeToken)state;//判断CancellationTokenSource是否被初始化过if (compositeChangeTokenState._cancellationTokenSource null){return;}//加锁 说明这一步是线程安全操作lock (compositeChangeTokenState._callbackLock){try{/*** 取消当前实例的CancellationTokenSource* 这样才能执行CompositeChangeToken注册的回调操作*/compositeChangeTokenState._cancellationTokenSource.Cancel();}catch{}}//获取EnsureCallbacksInitialized方法中注册的集合,即IChangeToken集合的回调返回值集合ListIDisposable disposables compositeChangeTokenState._disposables;//不为null则通过循环的方式挨个释放掉Debug.Assert(disposables ! null);for (int i 0; i disposables.Count; i){disposables[i].Dispose();}
}
通过上面的OnChange方法我们得知它主要是实现了在注册进来的任意IChangeToken实例如果发生了取消操作则当前的CompositeChangeToken实例RegisterChangeCallback进来的回调操作也要执行而且这一步要释放掉所有注册IChangeToken实例因为只要有一个IChangeToken实例执行了取消操作则CompositeChangeToken实例和其它注册进来相关联的IChangeToken实例都要取消。IChangeToken还有一个HasChange属性来标识当前IChangeToken是否被取消咱们来看下CompositeChangeToken是如何实现这个属性的[点击查看源码????[13]]public bool HasChanged
{get{//如果当前实例的CancellationTokenSource被取消过则说明当前CompositeChangeToken已被取消if (_cancellationTokenSource ! null _cancellationTokenSource.Token.IsCancellationRequested){return true;}//循环注册进来的关联的IChangeToken集合for (int i 0; i ChangeTokens.Count; i){//如果存在关联的IChangeToken实例有被取消的那么也认为当前CompositeChangeToken已被取消if (ChangeTokens[i].HasChanged){//调用OnChange是否关联的IChangeToken实例OnChange(this);return true;}}//否则则没被取消过return false;}
}
通过上面的代码可以看到HasChanged属性的设计思路符合它整体的设计思路。判断是否取消的标识有两个如果当前实例的CancellationTokenSource被取消过则说明当前CompositeChangeToken已被取消还有就是如果存在关联的IChangeToken实例有被取消的那么也认为当前CompositeChangeToken也被取消。好了通过上面这一部分整体的源码我们可以总结一下CompositeChangeToken的整体实现思路。•CompositeChangeToken的取消回调操作分为两部分一个是基于传递的IChangeToken集合中激活更改回调即ActiveChangeCallbacks为true的实例另一个则是它自身维护通过RegisterChangeCallback注册进来的委托这个委托是它内部维护的CancellationTokenSource实现的。•因为CompositeChangeToken的RegisterChangeCallback方法中给注册进来的IChangeToken集合中的每一个ActiveChangeCallbacks的实例注册了取消回调操作所以当ChangeToken静态类触发RegisterChangeCallback回调操作的时候回调用CompositeChangeToken的OnChange方法。•CompositeChangeToken的OnChange方法中会取消CompositeChangeToken内部维护的CancellationTokenSource也就是触发CompositeChangeToken类本身的回调并且释放注册进来的其他相关联的IChangeToken实例从而实现了关联取消的操作。 通过源码探究部分我们分别展示了关于IChangeToken接口以及它最简单的实现类CancellationChangeToken类的实现然后根据CancellationChangeToken类的实现讲解了ChangeToken静态类是如何实现动态令牌更改的最后又探究了IChangeToken接口的另一个高级的可以关联更改令牌操作的CompositeChangeToken的用法通过这样一个流程博主本人认为是更容易理解的。自定义IChangeToken实现上面我们看到了CancellationChangeToken的使用方式非常简单但是也存在一定的限制那就是需要外部传递CancellationTokenSource的实例。其实很多时候我们只需要知道你是IChangeToken实例就好了能满足被ChangeToken静态类使用就好了至于传递CancellationTokenSource啥的不需要外部关心能相应的操作就行了比如在.Net Core的Configuration体系中的ConfigurationReloadToken它是用来实现配置发生变化通知ConfigurationProvider重新加载数据完成自动刷新操作我们来看一下它的实现方式[点击查看源码????[14]]public class ConfigurationReloadToken : IChangeToken
{//内部定义了CancellationTokenSource实例private CancellationTokenSource _cts new CancellationTokenSource();public bool ActiveChangeCallbacks true;public bool HasChanged _cts.IsCancellationRequested;/// summary/// 给当前的CancellationTokenSource实例注册操作public IDisposable RegisterChangeCallback(Actionobject callback, object state) _cts.Token.Register(callback, state);/// summary/// 添加OnReload方法,供外部取消使用/// /summarypublic void OnReload() _cts.Cancel();
}
它在ConfigurationReloadToken类的内部声明了CancellationTokenSource类型的属性然后提供了可以取消CancellationTokenSource实例的方法OnReload这样的话逻辑可以在内部消化而不像在外部传递。当重新获取它的实例的时候额外提供一个可获取ConfigurationReloadToken新实例的方法即可[点击查看源码????[15]]private ConfigurationReloadToken _changeToken new ConfigurationReloadToken();
private void RaiseChanged()
{//直接交换一个新的ConfigurationReloadToken实例ConfigurationReloadToken previousToken Interlocked.Exchange(ref _changeToken, new ConfigurationReloadToken());//取消上一个ConfigurationReloadToken实例实现更改通知操作previousToken.OnReload();
}
这样的话获取Token的实现就非常简单了直接返回ConfigurationReloadToken的属性即可不再需要一堆额外的操作这样就可以保证每次通过GetReloadToken方法获取的IChangeToken实例都是未失效的。public IChangeToken GetReloadToken() _changeToken;
总结 本文我们讲解了ChangeToken相关的体系设计到了IChangeToken接口的几个实现类和ChangeToken静态类是如何实现通过取消令牌重复触发的其实本质也就是它的名字可以动态去更改令牌实现的大致思路就是类似递归的操作在回调通知里获取新的变更令牌实例重新注册当前回调操作形成递归。因为IChangeToken实例都是引用类型而我们传递的CancellationTokenSource实例也是引用类型所以我们在使用的时候没感觉有什么变化但其实如果你每次打印它的实例都是不一样的因为内部已经更换了新的实例。好了我们大致总结一下•IChangeToken接口是满足ChangeToken静态类的必须操作默认提供的CancellationChangeToken类则是IChangeToken接口最简单的实现它是依赖CancellationTokenSource实现注册和取消通知相关操作的。•ChangeToken静态类的工作依赖它的OnChange方法注册的参数一个是获取IChangeToken实例的委托一个是令牌取消执行的操作。其实现的本质是在CancellationChangeToken的Register方法里注册重新注册的操作。也就是通过ChangeToken静态类的OnChange方法第一个参数委托执行这个委托获取新的IChangeToken实例当然它包含的CancellationChangeToken实例也是最新的。然后ChangeToken静态类的OnChange方法第二个参数即回调操作重新注册给这个新的实例这个更改操作对外部都是无感知的但其实内部早已经更换了新的实例。•CompositeChangeToken实现关联取消更改操作的本质是给一堆IChangeToken实例注册相同的OnChange操作如果有一个IChangeToken实例执行了取消则通过OnChange方法取消当前CompositeChangeToken实例和相关联的IChangeToken实例防止同一个CompositeChangeToken实例重复被触发。这个系列我们讲解了取消令牌相关其核心都是对CancellationTokenSource的包装因为默认的CancellationTokenSource的实例默认只能被取消一次但是很多场景需要能多次甚至无限次触发这种通知比如.Net Core的Configuration体系每次配置发生变更都需要执行响应的刷新操作。因此衍生出来了IChangeToken相关结合辅助的ChangeToken来实现重复更改令牌的操作实现无限次的触发通知。虽然博主能力和文笔都十分有限但依然希望同学们能从中获取收获这也是作为写作人最大的动力。References[1] 浅谈C#取消令牌CancellationTokenSource: https://www.cnblogs.com/wucy/p/15128365.html[2] 点击查看源码????: https://hub.fastgit.org/dotnet/runtime/blob/v5.0.9/src/libraries/Microsoft.Extensions.Primitives/src/IChangeToken.cs[3] 点击查看源码????: https://hub.fastgit.org/dotnet/runtime/blob/v5.0.9/src/libraries/Microsoft.Extensions.Primitives/src/CancellationChangeToken.cs[4] 点击查看源码????: https://hub.fastgit.org/dotnet/runtime/blob/v5.0.9/src/libraries/Microsoft.Extensions.Primitives/src/ChangeToken.cs#L21[5] 点击查看源码????: https://hub.fastgit.org/dotnet/runtime/blob/v5.0.9/src/libraries/Microsoft.Extensions.Primitives/src/ChangeToken.cs#L65[6] 点击查看源码????: https://hub.fastgit.org/dotnet/runtime/blob/v5.0.9/src/libraries/Microsoft.Extensions.Primitives/src/ChangeToken.cs#L96[7] 点击查看源码????: https://hub.fastgit.org/dotnet/runtime/blob/v5.0.9/src/libraries/Microsoft.Extensions.Primitives/src/ChangeToken.cs#L76[8] 点击查看源码????: https://hub.fastgit.org/dotnet/runtime/blob/v5.0.9/src/libraries/Microsoft.Extensions.Primitives/src/ChangeToken.cs#L103[9] 点击查看源码????: https://hub.fastgit.org/dotnet/runtime/blob/v5.0.9/src/libraries/Microsoft.Extensions.Primitives/src/ChangeToken.cs#L136[10] 点击查看源码????: https://hub.fastgit.org/dotnet/runtime/blob/v5.0.9/src/libraries/Microsoft.Extensions.Primitives/src/CompositeChangeToken.cs#L26[11] 点击查看源码????: https://hub.fastgit.org/dotnet/runtime/blob/v5.0.9/src/libraries/Microsoft.Extensions.Primitives/src/CompositeChangeToken.cs#L45[12] 点击查看源码????: https://hub.fastgit.org/dotnet/runtime/blob/v5.0.9/src/libraries/Microsoft.Extensions.Primitives/src/CompositeChangeToken.cs#L105[13] 点击查看源码????: https://hub.fastgit.org/dotnet/runtime/blob/v5.0.9/src/libraries/Microsoft.Extensions.Primitives/src/CompositeChangeToken.cs#L52[14] 点击查看源码????: https://hub.fastgit.org/dotnet/runtime/blob/v5.0.9/src/libraries/Microsoft.Extensions.Configuration/src/ConfigurationReloadToken.cs[15] 点击查看源码????: https://hub.fastgit.org/dotnet/runtime/blob/v5.0.9/src/libraries/Microsoft.Extensions.Configuration/src/ConfigurationRoot.cs#L117