【五分钟的dotnet】是一个利用您的碎片化时间来学习和丰富.net知识的博文系列。它所包含了.net体系中可能会涉及到的方方面面,好比C#的小细节,AspnetCore,微服务中的.net知识等等。
5min+不是超过5分钟的意思,"+"是知识的增长。so,它是让您花费5分钟如下的时间来提高您的知识储备量。git
前段时间在阅读AspNet Core的源代码中,发现了一个叫作ChangeToken
的静态类。它的使用大概是这个样子:程序员
public ActionDescriptorCollectionProvider( IEnumerable<IActionDescriptorProvider> actionDescriptorProviders, IEnumerable<IActionDescriptorChangeProvider> actionDescriptorChangeProviders) { _actionDescriptorProviders = actionDescriptorProviders .OrderBy(p => p.Order) .ToArray(); _actionDescriptorChangeProviders = actionDescriptorChangeProviders.ToArray(); //here!! ChangeToken.OnChange( GetCompositeChangeToken, UpdateCollection); }
回想起来,这个东西我好像已经不止看到它一次两次了,在Microsoft.Extensions.FileProviders
包里面也有发现它的身影。迷惑了好久以后,今天总算能够找个机会来扒一扒它,看看它究竟是一个什么东西。github
其实,ChangeToken
在微软官方的AspNet Core教程文档中是有专门介绍它的文章:《使用 ASP.NET Core 中的更改令牌检测更改》。可是该篇文章我我的以为有点偏重于讲使用,而对原理比较淡化。这怎么能知足得了咱们程序员的探索欲😏,确定要再百度一波呀,而后……………… 又是只有一篇文章,仍是出自于我们园子。(手动博客园牛逼!)编程
其实,从MSDN里面的第一句描述以及这个类的命名,我们仍是能够读懂它的大体意思的。这不就是一个像观察者模式的东西吗? 当某某某发生变化的时候,就执行一个某某操做。安全
那么直接用委托订阅不行?我们先来想想使用传统的委托来进行操做是什么样子的?异步
Action myAction = () => { Console.WriteLine("人来了!"); }; myAction += () => { Console.WriteLine("狗要叫!"); }; myAction += () => { Console.WriteLine("猫要叫!"); };
相似于酱紫哈。当观察到人来了的时候,猫狗就都会叫起来。ide
可是这样写您会发现,其实上面demo中的三个事物(人、猫、狗)关联十分的密切。换成代码来理解的话,可能后期我们会创建三个类,而他们之间的交互都是直接引用来实现的。若是类型较多,简直会演变为一个噩梦。异步编程
那么有没有好的办法呢? 那确定是有的呀。微服务
我一直以为全部的代码都能用我们身边的小事来解释。因此,我又来说故事了😂。工具
先来回忆一下30年前,我们人与人之间是怎么联系的。额…………好像确实很难联系上。由于当时交通和通信工具都不发达,人们要交流只能经过见面。因此,当我想告诉某件事情给某人的时候,我必须亲自跑到他的家里,直到见到他本人或者与他的家人才可以完成。固然,还有一个好一点的办法就是托另外的一我的带个口信过去,可是这也必需要求我得见到这个中间人,还要信得过他。
在那个“通信基本靠吼; 交通基本靠走; 治安基本靠狗”的年代,声音大好像仍是有好处嘛。
那么如今咱们怎么联系呢? 我默默的从兜里摸出了波导手机(波导手机,手机中的战斗机,哦也)。这个社会,谁尚未一个手机呀,就算没有手机说不定也有电话手表。🤔
OK,回到上面的问题。您有没有一点灵感。 当一个类完成某个操做以后,下一个类就须要作出反应。刚开始,我们多是直接在A类里面显式的调用B类(只能亲自跑到他家去)。后来,咱们能够选择一个委托(找一个中间人带口信,或者邮递员等)。而如今,咱们能够选一个“手机”来实现了。
那么这个“手机”在代码里面是一个什么呢? 全部须要保持联系的人都得拥有它,只要“手机”在线就能进行通信,并且全部人都拿着“手机”你们都不会以为很奇怪? CancellationTokenSource
。像不像它,您是否在项目的大部分类里面都引入了它,而且没有感到任何一点的奇怪。
因此,当你们都承认这种相似于TokenSource
的东西以后,就以为很正常,虽然我们每次使用 CancellationTokenSource
都要引入System.Threading
命名空间。
CancellationTokenSource
来看看使用CancellationTokenSource
来触发一个观察动做:
var cancellationTokenSource = new CancellationTokenSource(); cancellationTokenSource.Token.Register(() => { Console.WriteLine($"{nameof(cancellationTokenSource)} 改变,触发了回调"); }); cancellationTokenSource.Cancel();
是否是很简单。我们只须要在须要的类里面引入CancellationTokenSource
就能够注册本身的回调方法,当它取消的时候就会执行响应的操做。加上CancellationTokenSource
自己的线程安全,因此它从一提出来就被普遍的应用于异步编程。
可能到这里您会问,这个和我们今天提到的ChangeToken
有半毛钱关系吗? 别急,我们慢慢来细看一下今天的主角:
public static class ChangeToken { public static IDisposable OnChange(Func<IChangeToken> changeTokenProducer, Action changeTokenConsumer); }
该类仅仅提供了一个OnChange
的静态方法,而该方法须要一个返回类型为IChangeToken
的参数。而一看这个命名**Token
,是否是很像我们上面的CancellationToken
,也就是说它可能就是一个我们公认的相似于“手机”同样的东西,拥有了它,就会获得通知。 是的,就是这个样子,这种东西官方的名称其实叫作“令牌”。因此,您可能都会猜到,它可能会具备一个注册回调的方法:
public interface IChangeToken { bool HasChanged { get; } bool ActiveChangeCallbacks { get; } IDisposable RegisterChangeCallback(Action<object> callback, object state); }
看起来好像很符合我们的猜测嘛。那么,它存在的意义是什么呢? 高层的抽象! 就好像咱们刚才所说的“手机”,手机是抽象的概念,而“OPPO手机”、“华为手机”、还有个人“波导手机”都是它的具体实现。咱们在不一样的圈子可能会使用不一样的手机。
好比下方的代码:
Console.WriteLine("开始监测文件夹 c:\\temp"); var phyFileProvider = new PhysicalFileProvider("c:\\temp"); IChangeToken changeToken = phyFileProvider.Watch("*.*"); changeToken.RegisterChangeCallback(_ => { Console.WriteLine("检测到文件夹有变化!" + _); }, "xiaoming"); Console.ReadLine();
code引用自:jackletter的博客
像不像一个叫作PhysicalFileProvider
的运营商,给我发了一个“手机”(令牌)。当我拥有这个令牌以后,运营商就能够联系到我了,当它联系个人时候,我就能够作出对应的反应。好比上面是打印一排字出来。
而在“物理文件”这个圈子里面,IChangeToken
的真身叫作PollingFileChangeToken
;在“配置系统”这个圈子里面,IChangeToken
的真身叫作ConfigurationReloadToken
。
若是我们想实现本身的IChangeToken
怎么办呢?还记得最上面的CancellationTokenSource
吗?既然.Net为我们提供了一个线程安全而又直接能够拿来用的工具,那咱们就不用客气了:
public class MyOwnChangeToken : IChangeToken { public CancellationTokenSource _cts = new CancellationTokenSource(); public bool ActiveChangeCallbacks => true; public bool HasChanged => _cts.IsCancellationRequested; public IDisposable RegisterChangeCallback(Action<object> callback, object state) => _cts.Token.Register(callback, state); public void MyOwnChange() => _cts.Cancel(); }
在“我本身的这个圈子”,就可使用MyOwnChangeToken
了,当外界获取到个人IChangeToken
,我就能够触发MyOwnChange
来通知他们了。
其实.NET Core中大部分的IChangeToken
内部都使用了CancellationTokenSource
。
搞懂了IChangeToken
咱们就很轻松就能理解了ChangeToken
,做为静态类的它,确定是做为一个工具类的实现。
说白了咱们直接使用静态方法就能够完成订阅了:
ChangeToken.OnChange( () => physicalFileProvider.Watch("*.*"), () => Console.WriteLine("检测到文件夹有变化!") );
那么您可能会说,我直接使用上文那个RegisterChangeCallback
方法订阅不行吗?他们有什么区别吗? 答案是:“调用次数”。使用RegisterChangeCallback
的方法,只会执行一次回调内容,由于当“令牌”用了一次以后,其实它就失效了。因此上面那个监控文件改动的代码,当第二次文件改动的时候,它实际上是不会再执行回调的。
而使用ChangeToken
这个静态类,它就能够帮助您不断的去获取新“令牌”而后注册对应的回调,因此就可以保证我们屡次改变也能触发回调了。
因此来看上面的这一段代码 ChangeToken.OnChange(() => physicalFileProvider.Watch("*.*"),...)
,“phyFileProvider
”这个“供应商”能够为咱们提供“令牌”,当该令牌发生改动的时候,咱们就有机会去完成操做了。() => physicalFileProvider.Watch("*.*")
这部分代码咱们能够称它为“令牌生产过程”,而() => Console.WriteLine("检测到文件夹有变化!")
叫作“令牌的消费过程”。ChangeToken
干的事情就是:当消费者消费以后,就又会去让“生产过程”再生成一个令牌出来,而且在该令牌上挂载“消费过程”,这样就能保证可以一直“观察”下去了。
其实ChangeToken
的实现很简单,有关它的源代码您能够参考:Github 源代码。
本期其实主要给你们介绍了IChangeToken
和ChangeToken
,关于IChangeToken
其实后期的文章中我们也有涉及到。它其实也是.net core中重要接口之一,理解它的“职责”和“原理”是颇有必要的。这样才能便于后期咱们学习它所在的“不一样圈子”,好比文中说起到的物理文件系统等。
最后,偷偷说一句:创做不易,点个推荐吧.....