ConcurrentBag <t>是合适的集合吗?</t>

时间:2014-05-06 21:51:18

标签: c# random concurrency asp.net-web-api thread-safety

我的情况是我在IIS上托管了一个ASP.NET Web API 2项目,而且我预计会出现并发问题。我需要生成随机数并确保它们是唯一的(稍后存储在数据库中)。为此,我实现了一个依赖于静态ConcurrentBag的简单内存中RNG。我知道这种实现意味着分布式架构存在风险。快速地,代码看起来像这样:

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事务来完成此任务不感兴趣。

2 个答案:

答案 0 :(得分:1)

我猜你试图解决并发问题,许多用户点击按钮来生成数字。虽然可以从并发角度使用ConcurrentBag,但我发现其他问题:

  • &#34;当订购无关紧要时,袋子可用于存放物品,而且不像套装,袋子支持重复。 &#34; msdn。我认为你试图避免重复。
  • 您需要为此序列设置某种受保护的部分或事务,否则可能会出现并发问题

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

我希望您不会推出自己的RNG,但重复使用RNGCryptoServiceProvider之类的内容。

答案 1 :(得分:0)

我修改了设计。我切换到ConcurrentDictionary以避免重复,如@oleksii所指出的那样。我使用一个字节,因为我没有使用该值,据我所知没有ConcurrentHashset

NUnit测试:

[Test]
public void GenerateStrings()
{
    var gen1 = new ConcurrentStringGenerator("0123456789", 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对于受保护的部分,我试图避免对序列的锁定语句,而是使用并发集合。您能否对以下声明更具体?

  

您需要拥有某种受保护的部分或事务   此序列,否则可能出现并发问题