【5min+】 一个令牌走天下!.Net Core中的ChangeToken

系列介绍

【五分钟的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 中的更改令牌检测更改》。可是该篇文章我我的以为有点偏重于讲使用,而对原理比较淡化。这怎么能知足得了咱们程序员的探索欲😏,确定要再百度一波呀,而后……………… 又是只有一篇文章,仍是出自于我们园子。(手动博客园牛逼!编程

x

观察者?

其实,从MSDN里面的第一句描述以及这个类的命名,我们仍是能够读懂它的大体意思的。这不就是一个像观察者模式的东西吗? 当某某某发生变化的时候,就执行一个某某操做。安全

那么直接用委托订阅不行?我们先来想想使用传统的委托来进行操做是什么样子的?异步

Action myAction = () => { Console.WriteLine("人来了!"); };
myAction += () => { Console.WriteLine("狗要叫!"); };
myAction += () => { Console.WriteLine("猫要叫!"); };

相似于酱紫哈。当观察到人来了的时候,猫狗就都会叫起来。ide

可是这样写您会发现,其实上面demo中的三个事物(人、猫、狗)关联十分的密切。换成代码来理解的话,可能后期我们会创建三个类,而他们之间的交互都是直接引用来实现的。若是类型较多,简直会演变为一个噩梦。异步编程

那么有没有好的办法呢? 那确定是有的呀。微服务

公认即合理?

我一直以为全部的代码都能用我们身边的小事来解释。因此,我又来说故事了😂。工具

先来回忆一下30年前,我们人与人之间是怎么联系的。额…………好像确实很难联系上。由于当时交通和通信工具都不发达,人们要交流只能经过见面。因此,当我想告诉某件事情给某人的时候,我必须亲自跑到他的家里,直到见到他本人或者与他的家人才可以完成。固然,还有一个好一点的办法就是托另外的一我的带个口信过去,可是这也必需要求我得见到这个中间人,还要信得过他。

在那个“通信基本靠吼; 交通基本靠走; 治安基本靠狗”的年代,声音大好像仍是有好处嘛。

x

那么如今咱们怎么联系呢? 我默默的从兜里摸出了波导手机(波导手机,手机中的战斗机,哦也)。这个社会,谁尚未一个手机呀,就算没有手机说不定也有电话手表。🤔

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 源代码

总结

本期其实主要给你们介绍了IChangeTokenChangeToken,关于IChangeToken其实后期的文章中我们也有涉及到。它其实也是.net core中重要接口之一,理解它的“职责”和“原理”是颇有必要的。这样才能便于后期咱们学习它所在的“不一样圈子”,好比文中说起到的物理文件系统等。

最后,偷偷说一句:创做不易,点个推荐吧.....

x

相关文章
相关标签/搜索