[英]thread safe Collection with upper bound
I am after a collection with the following properties: 我在具有以下属性的集合之后:
BlockingCollection<T>.TryAdd(T)
would be perfect, ie it would return false if the maximum number of elements has been reached BlockingCollection<T>.TryAdd(T)
相同的方法将是完美的,即如果已达到最大元素数,它将返回false ConcurrentDictionary
would be perfect, ie ability to identify elements by a key, remove any item (not just the first or last, which I think would be the limitation with BlockingCollection
) ConcurrentDictionary
是完美的,即通过键识别元素的能力,删除任何项目(不仅仅是第一个或最后一个,我认为这将是BlockingCollection
的限制) Before I attempt to roll my own, my questions are: 在我试图推销自己之前,我的问题是:
BlockingCollection
somehow? BlockingCollection
实现此功能? Finally, if I do need to try and make my own, what approach should I think about? 最后,如果我确实需要尝试自己做,我应该考虑什么方法? Is it as simple as a wrapped
Dictionary
with locks
? 它是否像带有
locks
的包装Dictionary
一样简单?
Example use: A chat room with a defined limit on number of participants could store the connection information of participants and reject new entrants until there is room to enter when full 使用示例:具有参与人数限定限制的聊天室可以存储参与者的连接信息并拒绝新参赛者,直到有足够的空间进入
Here is a simple implementation for this: 这是一个简单的实现:
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);
}
}
}
The simplest solution is just make a wrapper class that uses a normal dictionary and uses a ReaderWriterLockSlim
to control thread safe access. 最简单的解决方案是创建一个使用普通字典的包装类,并使用
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();
}
}
This class implements the full IDictionary<Tkey,TValue>
interface. 该类实现完整的
IDictionary<Tkey,TValue>
接口。 The way this works is all insertions pass through TryAdd
, if you are at or above the max size and try to insert a new member you get a false
from TryAdd
and a InvalidOperationException
from methods that do not return bool
. 这种方式的工作方式是所有插入都通过
TryAdd
,如果你达到或超过最大大小并尝试插入一个新成员,你会从TryAdd
获得一个false
,而来自不返回bool
方法的InvalidOperationException
。
The reason I did not use a ConcurrentDictionary
is there is no good way to try to check the count before adding a new member in an atomic way, so you would need to lock anyway. 我没有使用
ConcurrentDictionary
的原因是在以原子方式添加新成员之前尝试检查计数没有好办法,所以无论如何都需要锁定。 You could potentially use a concurrent dictionary and remove all of my EnterReadLock
's and replace the EnterWriteLock
's with normal lock
calls, but you would need to do performance testing to see which would do better. 您可以使用并发字典并删除我的所有
EnterReadLock
,并用正常的lock
调用替换EnterWriteLock
,但是您需要进行性能测试以查看哪个会做得更好。
If you want methods like GetOrAdd
it would not be hard to implement yourself. 如果你想要像
GetOrAdd
这样的方法,你自己就不会很难实现。
If you need to create something like a ConcurrentDictionary
with some extra features(eg max elements) I'd go for an Adaptor
that will hold a private ConcurrentDictionary
and expand it where you need to expand it. 如果你需要使用一些额外的功能(例如max elements)创建类似
ConcurrentDictionary
东西,我会选择一个包含私有ConcurrentDictionary
的Adaptor
,并在需要扩展它的地方展开它。
A lot of method calls will stay with no change(you will simple call your private ConcurrentDictionary
and do nothing). 许多方法调用将保持不变(您将简单地调用您的私有
ConcurrentDictionary
并且什么都不做)。
You'll end up with custom implementation anyways, that said there's no built in type that behaves dictionary-like and has capacity limitations... 无论如何,你最终会得到自定义实现,它表示没有类似字典的内置类型,并且具有容量限制......
To make it completely custom, you may go for ConcurrentHashSet
limiting amount of entries will work for you. 要使其完全自定义,您可以选择
ConcurrentHashSet
限制条目数量将适合您。
Concurrent HashSet<T> in .NET Framework? .NET Framework中的并发HashSet <T>?
If you have all these additional requirements isn't it better to create a class that composes a List
rather than is one? 如果你有所有这些额外的要求是不是更好的创建一个组成一个
List
而不是一个? Put the list inside the class you're making. 将列表放在您正在制作的课程中。
For example, I would say a chat room contains a list rather than being a special type of list. 例如,我会说聊天室包含一个列表而不是一个特殊类型的列表。 I would have all the max number, get chatter by name etc logic separate from the actual
list
. 我将拥有所有最大数量,通过名称等逻辑与实际
list
分开。 Then I would use a lock
around interactions with the list, or some threadsafe collection like ConcurrentBag
. 然后我会使用
lock
与列表的交互,或一些线程安全的集合,如ConcurrentBag
。 As far a whether you want a dictionary, it really depends on teh detail of the data and how you're going to be accessing it. 至于你是否想要一本字典,它实际上取决于数据的详细信息以及你将如何访问它。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.