2014-05-06 27 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语句和使用并发收集来代替。你能对以下陈述更具体吗?

你需要有某种受保护的区域或 该序列的交易,否则的并发性问题,可能会出现