这篇文章主要介绍了C#更改令牌ChangeToken,文中运用大量代码讲解的非常详细,感兴趣的小伙伴一起来看看这篇文章吧
简单实例
要想更好的了解一个新的知识,首先知道它是做啥的,其次要知道它怎么做。所以还是老规矩,咱们先通过简单的示例开始,这样更方便了解。ChangeToken
本身是一个静态类,它的核心入口OnChange
方法包含两个参数,一个是传递IChangeToken
接口实例来获取令牌,另一个是令牌取消之后进行的回调操作。博主本人知道的关于IChangeToken接口的在CLR中的实现类有两个,分别是CancellationChangeToken
和CompositeChangeToken
类,接下来咱们就分别介绍一下这两个类的简单使用。
CancellationChangeToken示例
咱们先来演示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示例
实际开发中很多时候都需要一些关联的场景,比如我触发了一个取消操作,我想把和这个相关联的其它操作也取消,也就是咱们说的有相关性。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接口
首先便是IChangeToken
接口,它是整个ChangeToken系列的入口操作,ChangeToken的OnChange操作也是通过它的实现类发起的。它的作用就是获取一个可以更改的令牌,也就是可以重复触发的令牌,咱们就先来看一下它的实现[点击查看源码👈]
public interface IChangeToken
{
/// <summary>
/// 用来标识是否发生过更改
/// </summary>
bool HasChanged { get; }
/// <summary>
/// 指示令牌是否支持回调
/// </summary>
bool ActiveChangeCallbacks { get; }
/// <summary>
/// 当令牌取消时执行的回调
/// </summary>
/// <param name="callback">回调执行委托</param>
/// <param name="state">回调委托的参数</param>
/// <returns>An <see cref="IDisposable"/> that is used to unregister the callback.</returns>
IDisposable RegisterChangeCallback(Action<object> callback, object state);
}
它定义的接口成员非常简单,总结起来就是两类,一个是判断是否发生过更改相关即取消操作,一个是发生过更改之后的回调操作。它只是定义一个标准任何实现这个标准的类都具备被ChangeToken的OnChange方法提供可更换令牌的能力。
CancellationChangeToken实现
上面我们了解了IChageToken接口定义的成员相关,而CancellationChangeToken
则是IChageToken接口最常使用默认的实现,了解了它的实现,我们就可以更好的知道ChangeToken的OnChange方法是如何工作的,所以这里我选择了先讲解CancellationChangeToken相关的实现,这样会让接下来的阅读变得更容易理解。好了直接看它的实现[点击查看源码👈]
public class CancellationChangeToken : IChangeToken
{
/// <summary>
/// 唯一构造函数通过CancellationChangeToken初始化
/// </summary>
public CancellationChangeToken(CancellationToken cancellationToken)
{
Token = cancellationToken;
}
/// <summary>
/// 因为它是通过CancellationToken实现具备回调的能力
/// </summary>
public bool ActiveChangeCallbacks { get; private set; } = true;
/// <summary>
/// 根据CancellationToken的IsCancellationRequested属性判断令牌是否已取消
/// </summary>
public bool HasChanged => Token.IsCancellationRequested;
/// <summary>
/// 接收传递进来的CancellationToken
/// </summary>
private CancellationToken Token { get; }
/// <summary>
/// 注册回调操作
/// </summary>
/// <returns></returns>
public IDisposable RegisterChangeCallback(Action<object> 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方法[点击查看源码👈]
public static IDisposable OnChange(Func<IChangeToken> changeTokenProducer, Action changeTokenConsumer)
{
return new ChangeTokenRegistration<Action>(changeTokenProducer, callback => callback(), changeTokenConsumer);
}
public static IDisposable OnChange<TState>(Func<IChangeToken> changeTokenProducer, Action<TState> changeTokenConsumer, TState state)
{
return new ChangeTokenRegistration<TState>(changeTokenProducer, changeTokenConsumer, state);
}
它的OnChange方法其实是包含两个重载的,一个是无参委托一个是有参委托。无参委托没啥好说的,通过有参回调我们可以给回调传递参数,这个参数是方法传递每次回调委托获取的值取决于State本身的值是什么。最重要的是它们两个都是返回了ChangeTokenRegistration<T>
类的实例,也就是说OnChange方法本身是一个外观用来隐藏ChangeTokenRegistration
private class ChangeTokenRegistration<TState> : IDisposable
{
//生产IChangeToken实例的委托
private readonly Func<IChangeToken> _changeTokenProducer;
//回调委托
private readonly Action<TState> _changeTokenConsumer;
//回调参数
private readonly TState _state;
public ChangeTokenRegistration(Func<IChangeToken> changeTokenProducer, Action<TState> changeTokenConsumer, TState state)
{
_changeTokenProducer = changeTokenProducer;
_changeTokenConsumer = changeTokenConsumer;
_state = state;
//执行changeTokenProducer得到IChangeToken实例
IChangeToken token = changeTokenProducer();
//调用RegisterChangeTokenCallback方法传递IChangeToken实例
RegisterChangeTokenCallback(token);
}
}
通过上面我们了解到ChangeTokenRegistration
正是实现ChangeToken效果的核心,而它的构造函数里通过执行传递进来产生IChangeToken新实例的委托得到了新的IChangeToken实例。这里需要注意,每次执行Func<IChangeToken>
都会得到新的IChangeToken实例。然后调用了RegisterChangeTokenCallback方法,而这个方法只需要传递得到的IChangeToken实例即可。接下来我们只需要看RegisterChangeTokenCallback方法实现即可[点击查看源码👈]
private void RegisterChangeTokenCallback(IChangeToken token)
{
//给IChangeToken实例注册回调操作
//回调操作正是执行当前ChangeTokenRegistration实例的OnChangeTokenFired方法
IDisposable registraton = token.RegisterChangeCallback(s => ((ChangeTokenRegistration<TState>)s).OnChangeTokenFired(), this);
SetDisposable(registraton);
}
从这里我们可以看出ChangeTokenRegistrationOnChangeTokenFired
方法,也就是说令牌取消的时候调用的就是OnChangeTokenFired方法,咱们直接看一下这个方法的实现[点击查看源码👈]
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的实现[点击查看源码👈]
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方法的实现,这样的话会让大家更好的理解它的这个释放体系[点击查看源码👈]
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实例也必须要释放。
通过上面讲解了ChangeTokenRegistration<TState>
类的实现我们了解到了ChangeToken类工作的本质,其实非常简单,为了怕大家没看明白在这里咱们简单的总结一下ChangeToken的整体工作过程。
- ChangeToken静态类只包装了
OnChange
方法,这个方法传递的核心参数是产生IChangeToken实例的委托和CancellationTokenSource实例取消后的回调操作。这里的IChangeToken实例和ChangeToken静态类没啥关系,就是名字长得像。 - ChangeToken静态类的OnChange方法本质是包装一个
ChangeTokenRegistration<TState>
实例。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,这样的话大家会很容易就理解这个操作方式的。咱们还是先从入口的构造函数入手吧[点击查看源码👈]
public class CompositeChangeToken : IChangeToken
{
public IReadOnlyList<IChangeToken> ChangeTokens { get; }
public bool ActiveChangeCallbacks { get; }
public CompositeChangeToken(IReadOnlyList<IChangeToken> 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方法,也就是说回调触发的操作就是这个方法,我们来看一下这个方法的实现[点击查看源码👈]
private CancellationTokenSource _cancellationTokenSource;
public IDisposable RegisterChangeCallback(Action<object> callback, object state)
{
//核心方法
EnsureCallbacksInitialized();
//这里的CancellationTokenSource注册CompositeChangeToken的回调操作
return _cancellationTokenSource.Token.Register(callback, state);
}
private static readonly Action<object> _onChangeDelegate = OnChange;
private bool _registeredCallbackProxy;
private List<IDisposable> _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 List<IDisposable>();
//循环要关联的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
方法实现[点击查看源码👈]
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集合的回调返回值集合
List<IDisposable> 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是如何实现这个属性的[点击查看源码👈]
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重新加载数据完成自动刷新操作,我们来看一下它的实现方式[点击查看源码👈]
public class ConfigurationReloadToken : IChangeToken
{
//内部定义了CancellationTokenSource实例
private CancellationTokenSource _cts = new CancellationTokenSource();
public bool ActiveChangeCallbacks => true;
public bool HasChanged => _cts.IsCancellationRequested;
/// <summary>
/// 给当前的CancellationTokenSource实例注册操作
public IDisposable RegisterChangeCallback(Action<object> callback, object state) => _cts.Token.Register(callback, state);
/// <summary>
/// 添加OnReload方法,供外部取消使用
/// </summary>
public void OnReload() => _cts.Cancel();
}
它在ConfigurationReloadToken类的内部声明了CancellationTokenSource类型的属性,然后提供了可以取消CancellationTokenSource实例的方法OnReload,这样的话逻辑可以在内部消化,而不像在外部传递。当重新获取它的实例的时候额外提供一个可获取ConfigurationReloadToken新实例的方法即可[点击查看源码👈]
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来实现重复更改令牌的操作,实现无限次的触发通知。虽然博主能力和文笔都十分有限,但依然希望同学们能从中获取收获,这也是作为写作人最大的动力。
到此这篇关于浅析C#更改令牌ChangeToken的文章就介绍到这了,更多相关C#更改令牌ChangeToken内容请搜索得得之家以前的文章希望大家以后多多支持得得之家!
本文标题为:浅析C#更改令牌ChangeToken
基础教程推荐
- ZooKeeper的安装及部署教程 2023-01-22
- unity实现动态排行榜 2023-04-27
- 一个读写csv文件的C#类 2022-11-06
- C#控制台实现飞行棋小游戏 2023-04-22
- C#类和结构详解 2023-05-30
- linux – 如何在Debian Jessie中安装dotnet core sdk 2023-09-26
- winform把Office转成PDF文件 2023-06-14
- C# windows语音识别与朗读实例 2023-04-27
- C# 调用WebService的方法 2023-03-09
- C# List实现行转列的通用方案 2022-11-02