2010-09-20 56 views
2

我有一個ASP.NET應用程序依賴隨機類來生成一個僞隨機字符串。它採用下面的代碼(這是由谷歌的服務提供一塊較大的樣本代碼的一部分應用程式SSO):隨機數發生器返回零

public static class SamlUtility 
{ 
    private static Random random = new Random(); 

    private static char[] charMapping = { 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o', 'p' }; 

    public static string CreateId() 
    { 
     byte[] bytes = new byte[20]; // 160 bits 

     random.NextBytes(bytes); 

     char[] chars = new char[40]; 

     for (int i = 0; i < bytes.Length; i++) 
     { 
      int left = (bytes[i] >> 4) & 0x0f; 
      int right = bytes[i] & 0x0f; 
      chars[i * 2] = charMapping[left]; 
      chars[i * 2 + 1] = charMapping[right]; 
     } 

     return new string(chars); 
    } 
} 

這通常工作得非常好,但不時它開始產生的「一個字符串」。從調試中我可以看出,Random只是停止返回隨機數,而是反覆填充具有相同值的字節。我已經通過使用GUID補丁了這一點,但我很好奇原始代碼中發生了什麼。我假設某種形式的熵枯竭,但我無法在文檔中找到任何參考。此外,每次發生這種情況時,執行iisreset都會恢復正確的行爲。

任何有關錯誤的建議將不勝感激。

+0

但零是別的隨機 - 見http://xkcd.com/221/ – 2010-09-20 18:19:02

+2

要添加到@馬克:http://dilbert.com/strips/comic/2001-10 -25/ – 2010-09-20 18:26:52

+1

有一天我們會得到一個涉及隨機數字和SQL注入的問題,那麼我們終於可以達到編程漫畫引用的三連勝了! – 2010-09-20 20:10:40

回答

6

Random類不是線程安全的。
如果您一次在多個線程上的同一實例上生成隨機數,其內部狀態將被破壞,並且它將開始返回零。

您需要使Random實例[ThreadStatic]確保每個實例不被多個線程共享。
請注意,[ThreadStatic]字段的初始化程序只能運行一次,因此每次使用該字段時都需要檢查它是否爲null,並在必要時對其進行初始化。
在種子中包含線程ID和當前時間以防止種子衝突也是一個好主意。

請注意,順便說一下,Random類是不安全的;考慮使用RNGCryptoServiceProvider class

+0

或者您可以用鎖保護單個隨機實例。 – 2010-09-20 18:18:33

+0

@Jim:應儘可能避免鎖。 (他們很慢) – SLaks 2010-09-20 18:20:31

+0

謝謝,SLaks!我只依靠隨機數來提供唯一性;是我的解決方案使用GUID一個可接受的解決方案,還是更有意義的恢復使用隨機並設置它ThreadStatic? – Jacob 2010-09-20 18:25:02