2010-10-13 75 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