我们的设置是:Asp.NET + MVC5,使用 AutoFac 进行 DI。
我们有一个类(单例),它管理各种服务的访问令牌。有时,这些令牌即将到期(不到 10 分钟),我们会请求新令牌并刷新它们。我当前的实现如下所示:
// member int used for interlocking
int m_inter = 0;
private string Token { get; set; }
private DateTimeOffset TokenExpiry { get; set; }
public SingletonClassConstructor()
{
// Make sure the Token has some value.
RefreshToken();
}
public string GetCredentials()
{
if ((TokenExpiry - DateTimeOffset.UTCNow).TotalMinutes < 10)
{
if (Interlocked.CompareExchange(ref m_inter, 1, 0) == 0)
{
RefreshToken();
m_inter = 0;
}
}
return Token;
}
private void RefreshToken()
{
// Call some stuff
Token = X.Result().Token;
TokenExpiry = X.Result().Expiry;
}
正如您所看到的,互锁确保只有一个线程通过,其余线程获取旧令牌。我想知道的是 - 我们是否会遇到一种奇怪的情况,即当令牌被覆盖时,另一个线程尝试读取而不是旧令牌,得到部分搞砸的结果?这个实现有什么问题吗?
Thanks!
对我来说,此实现的最大问题是您可能会在一个有效期内刷新令牌两次或多次。如果线程在检查过期条件之后但在执行之前被挂起CompareExchange()
,那么另一个线程可以完成刷新操作,包括重置m_inter
,在第一个线程恢复之前。理论上,这可能发生在任意多个线程上。
您的代码的其余部分不够具体,无法评论。没有任何声明Token
类型,所以不清楚这是否是struct
or class
。和你的GetCredentials()
方法被声明为返回一个Credentials
值,而是返回一个Token
值,因此该代码显然不是真正的代码。
If the Token
类型是一个class
,那么其余的实现可能就没问题了。即使在 x64 平台上,引用类型变量也可以原子分配,因此检索引用类型变量的代码Token
属性值将看到旧令牌或新令牌,而不是某些损坏的中间状态。 (当然,我假设Token
对象本身是线程安全的,最好是因为不可变。)
就我个人而言,我不会打扰CompareExchange()
。只需使用成熟的 C#lock
声明并完成它。将整个操作包含在同步块中:检查过期时间,必要时替换令牌,并返回令牌值,所有这些都来自于lock
.
根据您展示的代码,我认为将整个事物封装在属性本身中并使其更有意义public
。但不管怎样,只要代码检索令牌值就可以only通过这一段同步代码,证明代码正确的最简单、最可靠的方法就是使用lock
。万一您发现性能问题,那么您可以考虑更难正确执行的替代实现。
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)