在上篇文章浅谈C#取消令牌CancellationTokenSource一文中我们讲解了CancellationTokenSource,它的主要功能就是分发一个令牌,当我取消令牌我可以进行一些回调操作或者通过令牌状态得知被取消。在上文的结尾处我们也提到了,默认情况下CancellationTokenSource产生的Token是一次性的,Cancel操作之后就没办法再复用了,只能释放掉了。而微软也很贴心的为我们提供了一个解决方案来解决这问题,那就是我们今天要说的更改令牌ChangeToken,它看起来可以让CancellationTokenSource产生的Token多次触发,本文我们就来讲解ChangeToken相关。
要想更好的了解一个新的知识,首先知道它是做啥的,其次要知道它怎么做。所以还是老规矩,咱们先通过简单的示例开始,这样更方便了解。ChangeToken
本身是一个静态类,它的核心入口OnChange
方法包含两个参数,一个是传递IChangeToken
接口实例来获取令牌,另一个是令牌取消之后进行的回调操作。博主本人知道的关于IChangeToken接口的在CLR中的实现类有两个,分别是CancellationChangeToken
和CompositeChangeToken
类,接下来咱们就分别介绍一下这两个类的简单使用。
咱们先来演示CancellationChangeToken
类的使用方式,这也是默认情况下可以使用ChangeToken的最简单方式。首先定义一个TestCancellationChangeToken类来包装一下CancellationChangeToken,实现如下
public class TestCancellationChangeToken { private CancellationTokenSource tokenSource; /// <summary> /// 获取CancellationChangeToken实例方法 /// </summary> public CancellationChangeToken CreatChanageToken() { tokenSource = new CancellationTokenSource(); return new CancellationChangeToken(tokenSource.Token); } /// <summary> /// 取消CancellationTokenSource /// </summary> public 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
正是可以绑定一个相关性的IChangeToken集合,当这个IChangeToken集合中有任何一个实例进行取消操作的时候,当前CompositeChangeToken实例也会执行取消操作,咱们就大致演示一下它的使用方式,首先是定义一个使用类TestCompositeChangeToken来模拟包装CompositeChangeToken实例
public class TestCompositeChangeToken { //声明一个CancellationTokenSource集合 private List<CancellationTokenSource> _cancellationTokenSources; /// <summary> /// 获取CompositeChangeToken实例方法 /// </summary> public 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 List<CancellationTokenSource> { firstCancellationTokenSource, secondCancellationTokenSource, threeCancellationTokenSource }; //生成CancellationChangeToken集合 var cancellationChangeTokens = _cancellationTokenSources.Select(i => new CancellationChangeToken(i.Token)).ToList(); //传递给CompositeChangeToken var compositeChangeToken = new CompositeChangeToken(cancellationChangeTokens); //给CompositeChangeToken实例注册一个取消回调方便演示 compositeChangeToken.RegisterChangeCallback(state => System.Console.WriteLine("compositeChangeToken被取消"),null); return compositeChangeToken; } /// <summary> /// 取消CancellationTokenSource /// </summary> public 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
接口,它是整个ChangeToken系列的入口操作,ChangeToken的OnChange操作也是通过它的实现类发起的。它的作用就是获取一个可以更改的令牌,也就是可以重复触发的令牌,咱们就先来看一下它的实现[点击查看源码