使用线程安全性更新单例的属性 [英] Updating property of a singleton with Thread Safety

查看:97
本文介绍了使用线程安全性更新单例的属性的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我们的设置是:使用AutoFac for DI的Asp.NET + MVC5.

Our setup is: Asp.NET + MVC5 using AutoFac for DI.

我们有一个类(单例),它正在管理各种服务的访问令牌.这些令牌有时不时地接近到期(不到10分钟),因此我们要求提供新令牌,并对其进行刷新.我当前的实现如下所示:

We have a class (which is a singleton) which is managing the access tokens for a variety of services. Every now and then, these tokens get too close to expiry (less then 10 minutes) and we request new tokens, refresh them. My current implementation looks like this:

// 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;
}

如您所见,互锁"确保只有一个线程通过,其余线程获得了旧令牌.我想知道的是-我们会遇到一种怪异的情况吗?当令牌被覆盖时,另一个线程尝试读取,而不是旧的令牌,得到了部分搞砸的结果吗?此实现有任何问题吗?

As you can see the Interlocked makes sure only one thread goes through and the rest gets the old Token. What I'm wondering is - can we end up in a weird situation where when the Token is being overwritten, another thread tries to read and instead of the old token, gets a partial screwed up result? Any problems with this implementation?

谢谢!

推荐答案

对我来说,此实现的最大问题是,您可以在单个有效期内刷新令牌两次或更多次.如果在检查到期条件之后但在CompareExchange()之前一个线程被挂起,则另一个线程可能会一直执行刷新操作,包括重置m_inter,然后再恢复第一个线程.从理论上讲,这可以发生在任意多个线程上.

To me, the biggest issue with this implementation is that you may refresh the token two or more times for a single expiration period. If a thread is suspended just after checking the expiration condition but before the CompareExchange(), then another thread could get all the way through the refresh operation, including resetting m_inter, before that first thread is resumed. This can theoretically happen to arbitrarily many threads.

您其余的代码不够具体,无法评论.没有Token类型的声明,因此不清楚是struct还是class.并且您的GetCredentials()方法被声明为返回Credentials值,但返回了Token值,因此该代码显然甚至不是真实的代码.

The remainder of your code isn't specific enough to comment on. There's no declaration of the Token type, so it's not clear whether that's a struct or class. And your GetCredentials() method is declared as returning a Credentials value, but instead returns a Token value, so that code obviously isn't even real code.

如果Token类型是class,则其余的实现可能很好.即使在x64平台上,也可以原子方式分配引用类型变量,因此检索Token属性值的代码将看到旧令牌或新令牌,而不会看到某些损坏的中间状态. (当然,我假设Token对象本身是线程安全的,最好是因为它是不可变的.)

If the Token type is a class, then the rest of the implementation is probably fine. Reference type variables can be assigned atomically, even on x64 platforms, so code retrieving the Token property value will see either the old token or the new one, not some corrupted intermediate state. (I'm assuming, of course, that the Token object itself is thread-safe, preferably by virtue of being immutable.)

就我个人而言,我不会为CompareExchange()所困扰.只需使用成熟的C#lock语句即可完成.将整个操作包含在同步块中:检查到期时间,如有必要,替换令牌,并从lock内部返回令牌值.

Personally, I would not bother with CompareExchange(). Just use a full-blown C# lock statement and be done with it. Contain the entire operation in the synchronized block: check the expiration time, replace the token if necessary, and return the token value, all from within the lock.

根据您显示的代码,我认为将整个内容封装在属性本身中并使其变得更有意义.但是,无论哪种方式,只要检索令牌值的代码只能通过同步代码的此部分 来获取,证明代码正确的最简单,最可靠的方法就是使用.万一出现这种情况,可能会遇到性能问题,那么您可以考虑更难实现的替代实现.

Based on the code you showed, I would think it would make more sense to encapsulate the whole thing in the property itself and make that public. But one way or the other, as long as code retrieving the token value can only get it through this section of synchronized code, the easiest and most reliably way to prove the code is correct is to use lock. In the unlikely event that you see performance problems with that, then you can consider alternative implementations that are harder to get right.

这篇关于使用线程安全性更新单例的属性的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆