是ConcurrentBag适当的集合
本文关键字:集合 ConcurrentBag | 更新日期: 2023-09-27 18:01:57
我在一个情况下,我有一个ASP。. NET Web API 2项目托管在IIS上,我预计并发问题。我需要生成随机数并确保它们是唯一的(稍后存储在数据库中)。为此,我实现了一个简单的内存RNG,它依赖于静态ConcurrentBag。我知道这种实现对分布式架构意味着风险。很快,代码看起来像这样:
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事务来完成此任务不感兴趣。
我猜您试图解决许多用户单击按钮生成数字的并发问题。虽然从并发性的角度来看,ConcurrentBag
可能是可以使用的,但我看到了其他问题:
- "当订购无关紧要时,袋对于存储对象很有用,并且与集不同,袋支持副本。"msdn。我想你是想避免重复。
-
您需要为这个序列设置某种保护区段或事务,否则可能会出现并发问题
var number = rng.ReserveNumber(); StoreIntoDatabase(number); rng.ReleaseNumber(number);
我希望你不要推出自己的RNG,而是重用RNGCryptoServiceProvider
之类的东西。
我已经修改了设计。我切换到ConcurrentDictionary
以避免@oleksii指出的重复。我使用一个字节,因为我不使用该值,并且据我所知没有ConcurrentHashset
。
[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对于受保护的部分,我试图避免在序列上使用锁语句,而是使用并发集合。你能否具体说明以下说法?
你需要有某种类型的保护区段或事务此顺序,否则可能出现并发性问题