我收集了以下属性后:
> threadsafe:它将在asp.net中使用,多个客户端可以尝试同时添加,删除和访问成员
>最大元素:我想在构建时能够设置上限,最大元素数
> TryAdd:与BlockingCollection& T> .TryAdd(T)相同的方法将是完美的,即如果已达到最大数量的元素,则返回false
>词典:在大多数其他方面,ConcurrentDictionary将是完美的,即通过键识别元素的能力,删除任何项目(不仅仅是第一个或最后一个,我认为这将是BlockingCollection的限制)
在我试图自己滚动之前,我的问题是:
我错过了一个内置的类型,将放置在一个集合中的元素数量的安全上限?
>有没有办法用BlockingCollection来实现这个功能?
最后,如果我确实需要尝试自己,我应该考虑什么方法?它是一个简单的包装字典与锁?
使用示例
具有参与人数限定的聊天室可以存储参与者的连接信息并拒绝新进入者,直到有充分的空间进入
解决方法
最简单的解决方案是使一个包装类使用一个普通的字典,并使用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,如果您处于或大于最大大小,并尝试插入一个新成员,从TryAdd中收到一个false,并从不返回bool的方法中返回一个InvalidOperationException.
我没有使用ConcurrentDictionary的原因是在atomic的方式添加新成员之前,尝试检查计数是没有好办法的,所以你需要锁定.您可以使用并发字典并删除所有的EnterReadLock,并用正常的锁定调用替换EnterWriteLock,但是您需要进行性能测试,以确定哪些更好.
如果你想要GetOrAdd这样的方法,那就不难实现.