我在拥有以下属性的集合之后:
BlockingCollection<T>.TryAdd(T)
相同的方法是完美的,即如果达到最大元素数则返回false ConcurrentDictionary
将是完美的,即通过键识别元素的能力,删除任何项目(不只是第一个或最后一个,我认为是BlockingCollection
)在我尝试推销自己之前,我的问题是:
BlockingCollection
某种方式实现此功能?最后,如果我确实需要尝试自己做,我应该考虑什么方法?它是否与包裹Dictionary
的{{1}}一样简单?
使用示例: 具有参与人数限定限制的聊天室可以存储参与者的连接信息并拒绝新参赛者,直到有足够的空间进入
答案 0 :(得分:3)
这是一个简单的实现:
public class ConcurrentDictionaryEx<TKey, TValue>
{
private readonly object _lock = new object();
private ConcurrentDictionary<TKey, TValue> _dic;
public int Capacity { get; set; }
public int Count { get; set; }
public ConcurrentDictionaryEx(int capacity, int concurrencyLevel = 2)
{
this.Capacity = capacity;
_dic = new ConcurrentDictionary<TKey, TValue>(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);
}
}
}
答案 1 :(得分:2)
如果您需要创建具有一些额外功能的ConcurrentDictionary
之类的东西(例如最大元素),我会选择Adaptor
来保存私有ConcurrentDictionary
并将其扩展到你需要扩展它。
许多方法调用将保持不变(您只需调用私有ConcurrentDictionary
并且不执行任何操作)。
答案 2 :(得分:2)
最简单的解决方案是创建一个使用普通字典的包装类,并使用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
并{{1来自不返回InvalidOperationException
的方法。
我没有使用bool
的原因是在以atomic方式添加新成员之前尝试检查计数没有好办法,所以无论如何都需要锁定。您可以使用并发字典并删除我的所有ConcurrentDictionary
并将EnterReadLock
替换为普通的EnterWriteLock
调用,但您需要进行性能测试才能看到做得更好。
如果你想要像lock
这样的方法,你自己就不难实现。
答案 3 :(得分:1)
无论如何,你最终会得到自定义实现,它表示没有类似字典的内置类型,并且有容量限制......
要使其完全自定义,您可以选择ConcurrentHashSet
限制数量的条目对您有用。
答案 4 :(得分:1)
如果您有所有这些额外要求,那么创建一个composes一个List
而不是一个类的类是不是更好?将列表放在您正在制作的课程中。
例如,我会说聊天室包含一个列表而不是一个特殊类型的列表。我将获得所有最大数量,通过名称等逻辑与实际list
分开。然后,我会使用lock
与列表进行互动,或者使用threadsafe collection之类的ConcurrentBag
。至于你是否想要一本字典,它实际上取决于数据的详细信息以及你将如何访问它。