2014-05-06 30 views
0

我處於IIS上有一個ASP.NET Web API 2項目的情況,我在期待併發問題。我需要生成隨機數並確保它們是唯一的(稍後存儲在數據庫中)。爲此,我實現了一個簡單的內存RNG,它依賴於靜態ConcurrentBag。我意識到,這種實現將意味着分佈式架構上的風險。很快,代碼如下所示:ConcurrentBag <T>是否合適?

public interface IRandomNumberGenerator 
{ 
    string ReserveNumber(); 
    string ReleaseNumber(string number); 
} 

public class InMemoryRandomNumberGenerator : IRandomNumberGenerator 
{ 
    private static readonly ConcurrentBag<string> Bag = new ConcurrentBag<string>(); 

    public string ReserveNumber() 
    { 
     // Add 
     throw new NotImplementedException(); 
    } 

    public string ReleaseNumber(string number) 
    { 
     // Remove 
     throw new NotImplementedException(); 
    } 
} 

這段代碼的目的是,像這樣使用:

var number = rng.ReserveNumber(); 

StoreIntoDatabase(number); 

rng.ReleaseNumber(number); 

我是否適當地使用ConcurrentBag收藏?

另請注意,我簡化了示例,並且我不感興趣將代碼移入SQL並使用SQL事務來完成此任務。

+1

您可能需要打開'InMemoryRandomNumberGenerator'成'靜態class'。否則,你並沒有真正描述你想用'ConcurrentBag '來做什麼。我建議你嘗試一下,如你所提供的信息的形式,我可以看到沒有理由爲什麼它不合適。 –

+0

@KrisVandermotten感謝您的反饋Kris。將立即應用更改。 – maxbeaudoin

+1

當你試圖編譯時你肯定會意識到:一個靜態類不能實現一個接口...... – oleksii

回答

1

我想你試圖解決併發問題,其中許多用戶點擊一個按鈕來生成一個數字。雖然ConcurrentBag可能確定從併發性的角度用我看其他問題:

  • 「包包都是在訂貨時不要緊存儲對象是有用的,而不像套,包支持重複。」 msdn。我認爲你試圖避免重複。
  • 你需要有某種受保護的區域或該序列的交易,否則的併發性問題,可能會出現

    var number = rng.ReserveNumber(); 
    StoreIntoDatabase(number); 
    rng.ReleaseNumber(number); 
    

我希望你不要推出自己的RNG,而是重用像RNGCryptoServiceProvider

+0

@maxbeaudoin我記得寫了一個隨機字符串擴展,也許你會[看看](http://stackoverflow.com/a/8683325/706456)。還有其他一些替代答案。這是爲了防止你需要一個隨機字符串。 – oleksii

+0

我已經使用了一個非常類似的方法[本文]啓發(http://stackoverflow.com/questions/1344221/how-can-i-generate-random-alphanumeric-strings-in-c)。你能看看我的答案嗎? – maxbeaudoin

0

我修改了設計。我切換到ConcurrentDictionary以避免@oleksii指出的重複。我使用一個字節,因爲我不使用該值,並且據我所知沒有ConcurrentHashset

NUnit測試:

[Test] 
public void GenerateStrings() 
{ 
    var gen1 = new ConcurrentStringGenerator("", 9); 

    for (int i = 0; i < 100; i++) 
    { 
     var str = gen1.Reserve(); 
     Console.WriteLine(int.Parse(str).ToString("000-000-000")); 
     Assert.True(gen1.Release(str)); 
    } 

    var gen2 = new ConcurrentStringGenerator("ABCDEFGHJKLMNPQRSTUVWXYZ", 3); 

    for (int i = 0; i < 100; i++) 
    { 
     var str = gen2.Reserve(); 
     Console.WriteLine(str); 
     Assert.True(gen2.Release(str)); 
    } 
} 

實現:

public class ConcurrentStringGenerator 
{ 
    private readonly Random _random; 
    private readonly string _charset; 
    private readonly int _length; 
    private readonly ConcurrentDictionary<string, byte> _numbers; 

    public ConcurrentStringGenerator(string charset, int length) 
    { 
     _charset = charset; 
     _length = length; 
     _random = new Random(); 
     _numbers = new ConcurrentDictionary<string, byte>(); 
    } 

    public string Reserve() 
    { 
     var str = Generate(); 
     while (!_numbers.TryAdd(str, 0)) 
     { 
      str = Generate(); 
     } 
     return str; 
    } 

    public bool Release(string str) 
    { 
     byte b; 
     return _numbers.TryRemove(str, out b); 
    } 

    private string Generate() 
    { 
     return new string(Enumerable.Repeat(_charset, _length).Select(s => s[_random.Next(s.Length)]).ToArray()); 
    } 
} 

@oleksii作爲受保護的部分,我試圖避免在序列中的lock語句和使用併發收集來代替。你能對以下陳述更具體嗎?

你需要有某種受保護的區域或 該序列的交易,否則的併發性問題,可能會出現