2012-03-16 137 views
8

隨機提供商這裏是一個Skeet posting for a random provider的高潮:實現在.net 3.5

public static class RandomProvider 
{  
    private static int seed = Environment.TickCount; 

    private static ThreadLocal<Random> randomWrapper = new ThreadLocal<Random>(() => 
     new Random(Interlocked.Increment(ref seed)) 
    ); 

    public static Random GetThreadRandom() 
    { 
     return randomWrapper.Value; 
    } 
} 

我想用同樣的概念在.NET 3.5的項目,這樣的ThreadLocal是不是一種選擇。

你會如何修改代碼以有沒有的ThreadLocal的幫助下,線程安全的隨機提供商?

UPDATE

好吧,我與西蒙的[ThreadStatic]打算現在,因爲我的理解是最好的。有很多很好的信息可以在時間允許的情況下回顧和重新思考。謝謝大家!

public static class RandomProvider 
{ 
    private static int _seed = Environment.TickCount; 

    [ThreadStatic] 
    private static Random _random; 

    /// <summary> 
    /// Gets the thread safe random. 
    /// </summary> 
    /// <returns></returns> 
    public static Random GetThreadRandom() { return _random ?? (_random = new Random(Interlocked.Increment(ref _seed))); } 
} 
+1

還有一個後續up article:https://msmvps.com/blogs/jon_skeet/archive/2009/11/04/revisiting-randomness.aspx他討論了其他線程安全策略。 – CodesInChaos 2012-03-16 17:57:56

回答

8

可以使用ThreadStaticAttribute

[ThreadStatic] 
private static Random _random; 

private static Random Random 
{ 
    get 
    { 
     int seed = Environment.TickCount; 

     return _random ?? (_random = new Random(Interlocked.Increment(ref seed))) 
    } 
} 
+0

不錯,值得+1,但聽起來太容易了,在閱讀了Skeet的記錄之後。 ThreadLocal實現有什麼優勢呢? – Berryl 2012-03-16 17:39:12

+0

我在這個問題之前並不知道'ThreadLocal',但乍一看,我看到的唯一優點是'ThreadLocal'可以被賦予一個工廠方法來初始化這個值,所以你不必檢查' null' – Terkel 2012-03-16 17:44:32

+0

Skeets post似乎表明'ThreadLocal'有點快,但是在我自己的測試中,我無法重現這一點。我使用'ThreadLocal'作爲我的隨機生成器庫,但不幸的是它使用代碼合約,因此只能在.net 4上運行。 – CodesInChaos 2012-03-16 17:53:16

9

你會如何修改代碼以有沒有的ThreadLocal的幫助下,線程安全的隨機提供商?

喬恩回答你的問題,你鏈接到文章中:

使用一個實例,而且使用的每一個調用者要記住,而他們使用的隨機數發生器獲得鎖。這可以通過使用封鎖來簡化,但是在大量多線程的系統中,您仍然可能會浪費大量時間來等待鎖。

所以,只要每次在包裝中鎖定它,並在完成後解鎖。

如果是足夠便宜,太棒了,這是夠便宜。

如果不是夠便宜,那麼你有兩種選擇。首先,使它更便宜。其次,編寫了一個可以在不鎖定的情況下使用的僞隨機數生成器的線程安全實現

有許多的辦法讓它更便宜。例如,您可以交易空間時間;當程序啓動時,您可以生成一個包含十萬個隨機數的數組,然後編寫一個無鎖算法,從該數組中提取以前計算的隨機值。當你用完值時,在數組中生成另外的數十萬個值,並將新數組換成舊數組。

這有缺點,它的內存消耗較大的約十萬次以上可能是,而且每十萬的數字突然它變得非常慢,然後再次加速。如果這是不可接受的,那麼提出了一個可接受的策略。你是誰知道什麼是可以接受的表現,什麼不是。

或者,就像我說的,寫自己的,如果你不喜歡被提供給你的人。編寫一個具有可接受性能的隨機線程安全實現,並在多個線程中使用它。

我看到了Jon說的關於鎖定包裝的內容,但不知道代碼的外觀!

喜歡的東西:

sealed class SafeRandom 
{ 
    private Random random = new Random(); 
    public int Next() 
    { 
     lock(random) 
     { 
      return random.Next(); 
     } 
    } 
} 

現在每次調用下一次,你把鎖。 (總是鎖定私人對象;這樣你就知道是你的代碼是鎖定它的唯一代碼!)如果兩個線程同時調用Next,那麼「失敗者」會阻塞,直到「贏家」離開Next方法。

如果你願意,你甚至可以使SafeRandom對象的靜態類:

static class SafeRandom 
{ 
    private static Random random = new Random(); 
    public static int Next() 
    { 
     lock(random) 
     { 
      return random.Next(); 
     } 
    } 
} 

,現在你可以從任何線程調用SafeRandom.Next()。

+0

嗯,就像*你*知道你在說什麼: - ) – Berryl 2012-03-16 17:44:45

+0

是的,我看到了什麼喬恩說關於鎖定在包裝,但不知道代碼將如何看! – Berryl 2012-03-16 17:46:02

+0

@Berryl:看到我的更新。 – 2012-03-16 17:51:12

1

我剛剛閱讀了「C#深度」的鏈接,雖然我同意Random不是線程安全是一種痛苦,但實際上我會用不同的方法來擺脫問題,即性能原因。

實例化一個隨機引擎是相當沉重的作用,所以我寧願只保留一個實例,並使其通過使用鎖的線程安全:

public static class RandomProvider 
{  
    private static Random randomEngine = new Random(Environment.TickCount); 

    private static object randomLock = new object(); 

    public static int GetRandomValue() 
    { 
     lock(randomLock) 
     { 
      return randomEngine.Next(); 
     } 
    } 
} 

HTH

+0

你是不是想要發佈不同的代碼?這看起來像原來的4.0代碼,不是嗎? – Berryl 2012-03-16 17:48:48

+0

否:它不再使用ThreadLocal,也不使用ThreadStatic – 2012-03-16 17:51:25