线程安全收集与上限
我在具有以下属性的集合之后:
- threadsafe :它将在asp.net中使用,多个客户端可以尝试同时添加,删除和访问成员
- max elements :我希望能够在构造时设置上限,最大元素数
- TryAdd :与
BlockingCollection.TryAdd(T)
相同的方法将是完美的,即如果已达到最大元素数,它将返回false - 类似于字典 :在大多数其他方面,
ConcurrentDictionary
是完美的,即通过键识别元素的能力,删除任何项目(不仅仅是第一个或最后一个,我认为这将是BlockingCollection
的限制)
在我尝试推出自己的产品之前,我的问题是:
- 我是否错过了内置类型,它会对集合中的元素数量设置安全上限?
- 是否有办法以某种方式使用
BlockingCollection
实现此function?
最后,如果我确实需要尝试自己做,我应该考虑什么方法? 它是否像带有locks
的包装Dictionary
一样简单?
使用示例:具有参与人数限定限制的聊天室可以存储参与者的连接信息并拒绝新参赛者,直到有足够的空间进入
最简单的解决方案是创建一个使用普通字典的包装类,并使用ReaderWriterLockSlim
来控制线程安全访问。
public class SizeLimitedDictionary : IDictionary { private readonly int _maxSize; private readonly IDictionary _dictionary; private readonly ReaderWriterLockSlim _readerWriterLock; public SizeLimitedDictionary(int maxSize) { _maxSize = maxSize; _dictionary = new Dictionary(_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 item) { _readerWriterLock.EnterWriteLock(); try { if (_dictionary.Count >= _maxSize) return false; _dictionary.Add(item); } finally { _readerWriterLock.ExitWriteLock(); } return true; } public void Add(KeyValuePair 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 item) { _readerWriterLock.EnterReadLock(); try { return _dictionary.Contains(item); } finally { _readerWriterLock.ExitReadLock(); } } public void CopyTo(KeyValuePair[] array, int arrayIndex) { _readerWriterLock.EnterReadLock(); try { _dictionary.CopyTo(array, arrayIndex); } finally { _readerWriterLock.ExitReadLock(); } } public bool Remove(KeyValuePair 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 Keys { get { _readerWriterLock.EnterReadLock(); try { return _dictionary.Keys; } finally { _readerWriterLock.ExitReadLock(); } } } public ICollection Values { get { _readerWriterLock.EnterReadLock(); try { return _dictionary.Values; } finally { _readerWriterLock.ExitReadLock(); } } } public IEnumerator> GetEnumerator() { return _dictionary.GetEnumerator(); } IEnumerator IEnumerable.GetEnumerator() { return ((IEnumerable)_dictionary).GetEnumerator(); } }
该类实现完整的IDictionary
接口。 这种方式的工作方式是所有插入都通过TryAdd
,如果你达到或超过最大大小并尝试插入一个新成员,你会从TryAdd
得到一个false
,并从不返回bool
方法得到一个InvalidOperationException
。
我没有使用ConcurrentDictionary
的原因是在以primefaces方式添加新成员之前尝试检查计数没有好办法,所以无论如何都需要锁定。 您可以使用并发字典并删除所有的EnterReadLock
,并用正常的lock
调用替换EnterWriteLock
,但是您需要进行性能测试以查看哪个更好。
如果你想要像GetOrAdd
这样的方法,你自己就不难实现。
如果你需要使用一些额外的function(例如max elements)来创建类似ConcurrentDictionary
东西,我会选择一个包含私有ConcurrentDictionary
的Adaptor
,并在需要扩展它的地方扩展它。
许多方法调用将保持不变(您将简单地调用您的私有ConcurrentDictionary
并且什么都不做)。
这是一个简单的实现:
public class ConcurrentDictionaryEx { private readonly object _lock = new object(); private ConcurrentDictionary _dic; public int Capacity { get; set; } public int Count { get; set; } public ConcurrentDictionaryEx(int capacity, int concurrencyLevel = 2) { this.Capacity = capacity; _dic = new ConcurrentDictionary(concurrencyLevel, capacity); } public bool TryAdd(TKey key, TValue value) { lock (_lock) { if (this.Count < this.Capacity && _dic.TryAdd(key, value)) { this.Count++; return true; } return false; } } public bool TryRemove(TKey key, out TValue value) { lock (_lock) { if (_dic.TryRemove(key, out value)) { this.Count--; return true; } return false; } } public bool TryGetValue(TKey key, out TValue value) { lock (_lock) { return _dic.TryGetValue(key, out value); } } public bool TryUpdate(TKey key, TValue newValue, TValue comparisonValue) { lock (_lock) { return _dic.TryUpdate(key, newValue, comparisonValue); } } }
无论如何,你最终会得到自定义实现,它表示没有类似字典的内置类型,并且具有容量限制……
要使其完全自定义,您可以选择ConcurrentHashSet
限制条目数量将适合您。
.NET Framework中的并发HashSet
如果您有所有这些额外的要求,那么创建一个组成 List
而不是一个类的类不是更好吗? 将列表放在您正在制作的课程中。
例如,我会说聊天室包含一个列表而不是一个特殊类型的列表。 我将拥有所有最大数量,通过名称等逻辑与实际list
分开。 然后我会使用lock
与列表的交互,或一些线程安全的集合,如ConcurrentBag
。 至于你是否想要一本字典,它实际上取决于数据的详细信息以及你将如何访问它。