CancellationToken 与 CancellationChangeToken
CancellationToken vs CancellationChangeToken
CancellationToken
和CancellationChangeToken
有什么区别?我什么时候使用哪一个?
看来它们可以用于相同的目的。我错过了什么?
CancellationChangeToken
实现了 IChangeToken
并且它的构造函数接受一个 CancellationToken
有一个参数。
我想说的是,它提供了能够使用期望 IChangeToken
的方法以及期望 CancellationToken
的方法,同时使用相同的标记。
的一个(有点人为的)例子
var firstCancellationTokenSource = new CancellationTokenSource();
var secondCancellationTokenSource = new CancellationTokenSource();
var firstCancellationToken = firstCancellationTokenSource.Token;
var secondCancellationToken = secondCancellationTokenSource.Token;
var firstCancellationChangeToken = new CancellationChangeToken(firstCancellationToken);
var secondCancellationChangeToken = new CancellationChangeToken(secondCancellationToken);
var compositeChangeToken =
new CompositeChangeToken(
new List<IChangeToken>
{
firstCancellationChangeToken,
secondCancellationChangeToken
});
这里,一个CompositeChangeToken
,构造函数接受一个IChangeToken
的列表,用两个CancellationToken
实例化。如果没有 CancellationChangeToken
class.
那是不可能的
简答:
CancellationChangeToken
是 CancellationToken
的薄包装,它通过 HasChanged
属性.
公开令牌的取消状态
由于CancellationChangeToken
实现了IChangeToken
接口,它可以传递给任何对令牌变化做出反应的对象,例如IMemoryCache
:每个缓存条目都可以设置为过期在一组特定条件下,这些条件由 IChangeToken
个实例抽象。例如,这些条件可能是配置更改或用户的明确无效请求。
CancellationChangeToken
包装器可以传递给一个或多个缓存条目,以便在取消时逐出它们。开发者也可以决定直接使用 CancellationToken
;然而,与通过 IChangeToken
进行的抽象相比,这会使内部逻辑和外部 API 变得复杂,IChangeToken
非常干净,可扩展且工作正常。
长答案:
接口IChangeToken
IChangeToken
定义了一种追踪某种token的方法,检查它是否有改变(HasChanged
), 并可能在 更改.
后自动触发一些 回调
IChangeToken
有多种实现方式;例如,他们跟踪文件或配置选项。
CancellationChangeToken
的作用
A look at the source code 快速显示 CancellationChangeToken
是 CancellationToken
:
的薄 IChangeToken
包装器
public bool HasChanged => Token.IsCancellationRequested;
所以它的"changed"状态直接对应底层代币的取消状态。
乍一看,这似乎很奇怪:CancellationToken
已经 offers support for registering automatic callbacks,那么为什么要引入一个公开功能少得多的包装器?
好吧,实际上至少有一个用例需要一种抽象的方式来观察代币状态:
缓存
ASP.NET 核心提供不同的缓存方式;我将在这里使用 IMemoryCache
作为示例,它提供了一个简单的基于内存的内置 key/value 缓存,并且可以轻松使用。
为了减少对每个请求的性能影响,缓存经常需要的、昂贵的计算是有意义的。但是,需要一种方法来跟踪活动的缓存条目,以便在它们过时时立即将其驱逐。问题是,缓存条目可能有非常不同的过时条件,例如它们依赖于配置值或本地文件(并且需要在这些配置值或本地文件更改后立即更新),或者有规律的超时。
IChangeToken
接口提供了一种简单的方法来抽象这些驱逐条件,这样缓存只需要检查给定 IChangeToken
对象的状态,而不是直接查看文件、计时器, 等等。缓存甚至可能会注册一个回调,这样令牌状态的改变会直接触发驱逐逻辑,因此不需要轮询。
回到我们的CancellationChangeToken
一个CancellationToken
允许设置一个延迟,直到它被自动取消,所以在用CancellationChangeToken
包装它之后,我们可以将它传递给一个缓存条目以进行自动驱逐:
public IActionResult Index()
{
if(!_memoryCache.TryGetValue("entry1", out DateTime cachedValue))
{
cachedValue = DateTime.Now;
var cacheOptions = new MemoryCacheEntryOptions()
.AddExpirationToken(new CancellationChangeToken(new CancellationTokenSource(5000).Token));
_memoryCache.Set("entry1", cachedValue, cacheOptions);
}
return View("Index", cachedValue);
}
在这个例子中,我们缓存当前时间戳。 5 秒后,取消令牌被取消,时间戳被逐出。
但是,这还不能证明使用 CancellationToken
是合理的,因为 IMemoryCache
已经提供了一种通过 MemoryCacheEntryOptions
.[=50= 设置过期时间的方法]
现在我们了解基于令牌的驱逐的真正优势:我们可以
- 将多个
IChangeToken
实例合并为一个 CompositeChangeToken
,从而在这些实例中的任何一个发生变化时逐出缓存条目。
- 对多个缓存条目使用相同的令牌,从而一次性将它们逐出。
后一种情况对我们来说特别有趣:一些缓存条目可能相互依赖,所以当一个变得无效时,我们可能想要驱逐所有它们。这可以通过将共享 CancellationToken
分配给它们的 值 并将相应的 CancellationChangeToken
分配给 条目 本身来实现。一旦缓存的 values 之一注意到它已经过时(例如,被应用程序的其他部分轮询),它就会取消令牌。取消传播到所有跟踪相应更改令牌的缓存条目,从而一次性使它们全部失效。
CancellationToken
被设计为 readonly struct (我假设这是由于性能原因)所以它不能扩展(继承)。所以如果你想对取消标记使用抽象,你不能使用CancellationToken
struct。为此,您必须使用 IChangeToken
.
所以到了classCancellationChangeToken
。它是一个 class(不是 struct as CancellationToken
) 并且它实现了 IChangeToken
interface;它包装了一个 CancellationToken
struct (真正的取消标记)
所以,如果我们想使用取消令牌的抽象,我们必须使用 IChangeToken
及其实现 ej: CancellationChangeToken
.
何时使用 CancellationToken
或 CancellationChangeToken
?
如果我们只关心更改通知,我们可以使用 CancellationToken
。
但是,如果我们想使用取消令牌作为抽象,我们必须使用 IChangeToken
及其实现(CancellationChangeToken
,等等)
CancellationToken
和CancellationChangeToken
有什么区别?我什么时候使用哪一个?
看来它们可以用于相同的目的。我错过了什么?
CancellationChangeToken
实现了 IChangeToken
并且它的构造函数接受一个 CancellationToken
有一个参数。
我想说的是,它提供了能够使用期望 IChangeToken
的方法以及期望 CancellationToken
的方法,同时使用相同的标记。
var firstCancellationTokenSource = new CancellationTokenSource();
var secondCancellationTokenSource = new CancellationTokenSource();
var firstCancellationToken = firstCancellationTokenSource.Token;
var secondCancellationToken = secondCancellationTokenSource.Token;
var firstCancellationChangeToken = new CancellationChangeToken(firstCancellationToken);
var secondCancellationChangeToken = new CancellationChangeToken(secondCancellationToken);
var compositeChangeToken =
new CompositeChangeToken(
new List<IChangeToken>
{
firstCancellationChangeToken,
secondCancellationChangeToken
});
这里,一个CompositeChangeToken
,构造函数接受一个IChangeToken
的列表,用两个CancellationToken
实例化。如果没有 CancellationChangeToken
class.
简答:
CancellationChangeToken
是 CancellationToken
的薄包装,它通过 HasChanged
属性.
由于CancellationChangeToken
实现了IChangeToken
接口,它可以传递给任何对令牌变化做出反应的对象,例如IMemoryCache
:每个缓存条目都可以设置为过期在一组特定条件下,这些条件由 IChangeToken
个实例抽象。例如,这些条件可能是配置更改或用户的明确无效请求。
CancellationChangeToken
包装器可以传递给一个或多个缓存条目,以便在取消时逐出它们。开发者也可以决定直接使用 CancellationToken
;然而,与通过 IChangeToken
进行的抽象相比,这会使内部逻辑和外部 API 变得复杂,IChangeToken
非常干净,可扩展且工作正常。
长答案:
接口IChangeToken
IChangeToken
定义了一种追踪某种token的方法,检查它是否有改变(HasChanged
), 并可能在 更改.
IChangeToken
有多种实现方式;例如,他们跟踪文件或配置选项。
CancellationChangeToken
的作用
A look at the source code 快速显示 CancellationChangeToken
是 CancellationToken
:
IChangeToken
包装器
public bool HasChanged => Token.IsCancellationRequested;
所以它的"changed"状态直接对应底层代币的取消状态。
乍一看,这似乎很奇怪:CancellationToken
已经 offers support for registering automatic callbacks,那么为什么要引入一个公开功能少得多的包装器?
好吧,实际上至少有一个用例需要一种抽象的方式来观察代币状态:
缓存
ASP.NET 核心提供不同的缓存方式;我将在这里使用 IMemoryCache
作为示例,它提供了一个简单的基于内存的内置 key/value 缓存,并且可以轻松使用。
为了减少对每个请求的性能影响,缓存经常需要的、昂贵的计算是有意义的。但是,需要一种方法来跟踪活动的缓存条目,以便在它们过时时立即将其驱逐。问题是,缓存条目可能有非常不同的过时条件,例如它们依赖于配置值或本地文件(并且需要在这些配置值或本地文件更改后立即更新),或者有规律的超时。
IChangeToken
接口提供了一种简单的方法来抽象这些驱逐条件,这样缓存只需要检查给定 IChangeToken
对象的状态,而不是直接查看文件、计时器, 等等。缓存甚至可能会注册一个回调,这样令牌状态的改变会直接触发驱逐逻辑,因此不需要轮询。
回到我们的CancellationChangeToken
一个CancellationToken
允许设置一个延迟,直到它被自动取消,所以在用CancellationChangeToken
包装它之后,我们可以将它传递给一个缓存条目以进行自动驱逐:
public IActionResult Index()
{
if(!_memoryCache.TryGetValue("entry1", out DateTime cachedValue))
{
cachedValue = DateTime.Now;
var cacheOptions = new MemoryCacheEntryOptions()
.AddExpirationToken(new CancellationChangeToken(new CancellationTokenSource(5000).Token));
_memoryCache.Set("entry1", cachedValue, cacheOptions);
}
return View("Index", cachedValue);
}
在这个例子中,我们缓存当前时间戳。 5 秒后,取消令牌被取消,时间戳被逐出。
但是,这还不能证明使用 CancellationToken
是合理的,因为 IMemoryCache
已经提供了一种通过 MemoryCacheEntryOptions
.[=50= 设置过期时间的方法]
现在我们了解基于令牌的驱逐的真正优势:我们可以
- 将多个
IChangeToken
实例合并为一个CompositeChangeToken
,从而在这些实例中的任何一个发生变化时逐出缓存条目。 - 对多个缓存条目使用相同的令牌,从而一次性将它们逐出。
后一种情况对我们来说特别有趣:一些缓存条目可能相互依赖,所以当一个变得无效时,我们可能想要驱逐所有它们。这可以通过将共享 CancellationToken
分配给它们的 值 并将相应的 CancellationChangeToken
分配给 条目 本身来实现。一旦缓存的 values 之一注意到它已经过时(例如,被应用程序的其他部分轮询),它就会取消令牌。取消传播到所有跟踪相应更改令牌的缓存条目,从而一次性使它们全部失效。
CancellationToken
被设计为 readonly struct (我假设这是由于性能原因)所以它不能扩展(继承)。所以如果你想对取消标记使用抽象,你不能使用CancellationToken
struct。为此,您必须使用 IChangeToken
.
所以到了classCancellationChangeToken
。它是一个 class(不是 struct as CancellationToken
) 并且它实现了 IChangeToken
interface;它包装了一个 CancellationToken
struct (真正的取消标记)
所以,如果我们想使用取消令牌的抽象,我们必须使用 IChangeToken
及其实现 ej: CancellationChangeToken
.
何时使用 CancellationToken
或 CancellationChangeToken
?
如果我们只关心更改通知,我们可以使用 CancellationToken
。
但是,如果我们想使用取消令牌作为抽象,我们必须使用 IChangeToken
及其实现(CancellationChangeToken
,等等)