2010-10-13 73 views
1

我需要一个身份验证令牌线程和同步安全。令牌将每小时过期,因此需要创建新令牌并将其分配给我的静态变量(TOKEN)此代码线程和同步安全吗?

这是否有用?

感谢,

public static volatile string TOKEN = string.Empty; 
    public static DateTime TOKEN_TIME = DateTime.Now; 
    private static readonly object syncRoot = new object(); 

    public static string Get() 
    { 
     if (!string.IsNullOrEmpty(TOKEN)) 
     { 
      if (!TokenIsValid()) 
      { 
       lock(syncRoot) 
        TOKEN = CreateNewToken(); 
      }     
     } 
     else 
     { 
      lock(syncRoot) 
       TOKEN = CreateNewToken(); 
     } 

     return TOKEN; 
    }   

回答

4

不,代码不是线程安全的。由于锁在if语句内部发生,因此两个线程可能几乎同时创建一个令牌。考虑以下几点:

  • 线程1进入else
  • 线程1个产量主题2
  • 线程2进入else
  • 线程2呈现syncRoot锁,创建一个新的令牌(令牌A),并将其分配给TOKEN
  • 线程2返回“令牌A”。
  • 线程2的产率主题1
  • 线程1花费锁syncRoot,创建一个新的令牌(令牌B),并将其分配给TOKEN
  • 线程1返回 “标记B”。

您的系统现在使用两个不同的标记,这两个标记仅在瞬间间隔创建,而Thread引用“标记B”。

编辑:
您可以通过采取锁之前,你甚至检查令牌使你的代码线程安全的。下面的例子锁定了对Get()的每次调用,所以它不会像您的代码那样在(几乎)同时创建两个令牌。还有其他锁定模式可以使用,其中一些可能会提供更好的性能。

public static string Get() 
{ 
    lock(syncRoot) 
    { 
     if (string.IsNullOrEmpty(TOKEN) || !TokenIsValid()) 
      TOKEN = CreateNewToken(); 
     return TOKEN; 
    } 
} 
+0

“if”分支也会出现同样的情况。作为一种解决方案,我将提供用一个锁来包装整个'Get()'主体。 – zerkms 2010-10-13 02:08:05

+2

更好的性能(仍然只是使用'lock')的例子是使用双重检查锁定:http://en.wikipedia.org/wiki/Double-checked_locking。尽管如此,请仔细阅读那里的注意事项。 – 2010-10-13 02:52:53