我正在寻找具有以下属性的集合:
- threadsafe:将在 asp.net 中使用,多个客户端可以同时尝试添加、删除和访问成员
- max elements:我希望能够在构造时设置一个上限,最大元素数
- TryAdd:一种与
BlockingCollection<T>.TryAdd(T)
相同的方法将是完美的,即如果已达到最大元素数,它将返回 false - 类似字典:在大多数其他方面是
ConcurrentDictionary
将是完美的,即能够通过键识别元素,删除任何项目(不仅仅是第一个或最后一个,我认为这将是BlockingCollection
的限制)
在尝试自己动手之前,我的问题是:
- 我是不是错过了一个可以对集合中的元素数量设置安全上限的内置类型?
- 有没有办法用
BlockingCollection
实现这个功能?不知何故?
最后,如果我确实需要尝试自己制作,我应该考虑什么方法?是否像包装一样简单 Dictionary
与 locks
?
使用示例: 具有定义的参与者数量限制的聊天室可以存储参与者的连接信息并拒绝新进入者,直到有足够的空间进入为止
最佳答案
最简单的解决方案就是制作一个使用普通字典并使用 ReaderWriterLockSlim
的包装类。控制线程安全访问。
public class SizeLimitedDictionary<TKey, TValue> : IDictionary<TKey, TValue>
{
private readonly int _maxSize;
private readonly IDictionary<TKey, TValue> _dictionary;
private readonly ReaderWriterLockSlim _readerWriterLock;
public SizeLimitedDictionary(int maxSize)
{
_maxSize = maxSize;
_dictionary = new Dictionary<TKey, TValue>(_maxSize);
_readerWriterLock = new ReaderWriterLockSlim();
}
public bool TryAdd(TKey key, TValue value)
{
_readerWriterLock.EnterWriteLock();
try
{
if (_dictionary.Count >= _maxSize)
return false;
_dictionary.Add(key, value);
}
finally
{
_readerWriterLock.ExitWriteLock();
}
return true;
}
public void Add(TKey key, TValue value)
{
bool added = TryAdd(key, value);
if(!added)
throw new InvalidOperationException("Dictionary is at max size, can not add additional members.");
}
public bool TryAdd(KeyValuePair<TKey, TValue> item)
{
_readerWriterLock.EnterWriteLock();
try
{
if (_dictionary.Count >= _maxSize)
return false;
_dictionary.Add(item);
}
finally
{
_readerWriterLock.ExitWriteLock();
}
return true;
}
public void Add(KeyValuePair<TKey, TValue> item)
{
bool added = TryAdd(item);
if (!added)
throw new InvalidOperationException("Dictionary is at max size, can not add additional members.");
}
public void Clear()
{
_readerWriterLock.EnterWriteLock();
try
{
_dictionary.Clear();
}
finally
{
_readerWriterLock.ExitWriteLock();
}
}
public bool Contains(KeyValuePair<TKey, TValue> item)
{
_readerWriterLock.EnterReadLock();
try
{
return _dictionary.Contains(item);
}
finally
{
_readerWriterLock.ExitReadLock();
}
}
public void CopyTo(KeyValuePair<TKey, TValue>[] array, int arrayIndex)
{
_readerWriterLock.EnterReadLock();
try
{
_dictionary.CopyTo(array, arrayIndex);
}
finally
{
_readerWriterLock.ExitReadLock();
}
}
public bool Remove(KeyValuePair<TKey, TValue> item)
{
_readerWriterLock.EnterWriteLock();
try
{
return _dictionary.Remove(item);
}
finally
{
_readerWriterLock.ExitWriteLock();
}
}
public int Count
{
get
{
_readerWriterLock.EnterReadLock();
try
{
return _dictionary.Count;
}
finally
{
_readerWriterLock.ExitReadLock();
}
}
}
public bool IsReadOnly
{
get
{
_readerWriterLock.EnterReadLock();
try
{
return _dictionary.IsReadOnly;
}
finally
{
_readerWriterLock.ExitReadLock();
}
}
}
public bool ContainsKey(TKey key)
{
_readerWriterLock.EnterReadLock();
try
{
return _dictionary.ContainsKey(key);
}
finally
{
_readerWriterLock.ExitReadLock();
}
}
public bool Remove(TKey key)
{
_readerWriterLock.EnterWriteLock();
try
{
return _dictionary.Remove(key);
}
finally
{
_readerWriterLock.ExitWriteLock();
}
}
public bool TryGetValue(TKey key, out TValue value)
{
_readerWriterLock.EnterReadLock();
try
{
return _dictionary.TryGetValue(key, out value);
}
finally
{
_readerWriterLock.ExitReadLock();
}
}
public TValue this[TKey key]
{
get
{
_readerWriterLock.EnterReadLock();
try
{
return _dictionary[key];
}
finally
{
_readerWriterLock.ExitReadLock();
}
}
set
{
_readerWriterLock.EnterUpgradeableReadLock();
try
{
var containsKey = _dictionary.ContainsKey(key);
_readerWriterLock.EnterWriteLock();
try
{
if (containsKey)
{
_dictionary[key] = value;
}
else
{
var added = TryAdd(key, value);
if(!added)
throw new InvalidOperationException("Dictionary is at max size, can not add additional members.");
}
}
finally
{
_readerWriterLock.ExitWriteLock();
}
}
finally
{
_readerWriterLock.ExitUpgradeableReadLock();
}
}
}
public ICollection<TKey> Keys
{
get
{
_readerWriterLock.EnterReadLock();
try
{
return _dictionary.Keys;
}
finally
{
_readerWriterLock.ExitReadLock();
}
}
}
public ICollection<TValue> Values
{
get
{
_readerWriterLock.EnterReadLock();
try
{
return _dictionary.Values;
}
finally
{
_readerWriterLock.ExitReadLock();
}
}
}
public IEnumerator<KeyValuePair<TKey, TValue>> GetEnumerator()
{
return _dictionary.GetEnumerator();
}
IEnumerator IEnumerable.GetEnumerator()
{
return ((IEnumerable)_dictionary).GetEnumerator();
}
}
此类实现了完整的 IDictionary<Tkey,TValue>
界面。它的工作方式是所有插入都通过 TryAdd
,如果您达到或超过最大大小并尝试插入新成员,您会得到一个 false
来自 TryAdd
和一个 InvalidOperationException
来自不返回 bool
的方法.
我没有使用 ConcurrentDictionary
的原因在 atomic 中添加新成员之前,是否没有好的方法来尝试检查计数?方式,因此无论如何您都需要锁定。您可能会使用并发字典并删除我所有的 EnterReadLock
的并替换 EnterWriteLock
正常 lock
调用,但您需要进行性能测试以查看哪个效果更好。
如果你想要类似 GetOrAdd
的方法自己实现并不难。
关于c# - 具有上限的线程安全集合,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/27403530/