在尝试添加新项之前,检查BlockingCollection
中是否存在某个项的最佳方法是什么?基本上,我不希望将重复项添加到BlockingCollection
中。
在尝试添加新项之前,检查BlockingCollection
中是否存在某个项的最佳方法是什么?基本上,我不希望将重复项添加到BlockingCollection
中。
你需要实现自己的 IProducerConsumerCollection<T>
,它的行为类似于集合(例如,不允许重复项)。这里是一个使用临界区(C# lock
)使其线程安全的简单版本。对于高并发情况,你可以像 ConcurrentQueue<T>
一样使用 SpinWait
类来提高性能。
public class ProducerConsumerSet<T> : IProducerConsumerCollection<T> {
readonly object gate = new object();
readonly Queue<T> queue = new Queue<T>();
readonly HashSet<T> hashSet = new HashSet<T>();
public void CopyTo(T[] array, int index) {
if (array == null)
throw new ArgumentNullException("array");
if (index < 0)
throw new ArgumentOutOfRangeException("index");
lock (gate)
queue.CopyTo(array, index);
}
public bool TryAdd(T item) {
lock (gate) {
if (hashSet.Contains(item))
return false;
queue.Enqueue(item);
hashSet.Add(item);
return true;
}
}
public bool TryTake(out T item) {
lock (gate) {
if (queue.Count == 0) {
item = default(T);
return false;
}
item = queue.Dequeue();
hashSet.Remove(item);
return true;
}
}
public T[] ToArray() {
lock (gate)
return queue.ToArray();
}
public void CopyTo(Array array, int index) {
if (array == null)
throw new ArgumentNullException("array");
lock (gate)
((ICollection) queue).CopyTo(array, index);
}
public int Count {
get { return queue.Count; }
}
public object SyncRoot {
get { return gate; }
}
public bool IsSynchronized {
get { return true; }
}
public IEnumerator<T> GetEnumerator() {
List<T> list = null;
lock (gate)
list = queue.ToList();
return list.GetEnumerator();
}
IEnumerator IEnumerable.GetEnumerator() {
return GetEnumerator();
}
}
IEqualityComparer<T>
来定制相等性,然后用于初始化 HashSet<T>
。
IProducerConsumerCollection<T>.Add
方法在尝试插入重复项时返回 false
。这会导致 BlockingCollection<T>.Add
方法引发 InvalidOperationException
,所以您可能需要将添加项目的代码包装到类似以下内容的代码中:bool AddItem<T>(BlockingCollection<T> blockingCollection, T item) {
try {
blockingCollection.Add(item);
return true;
}
catch (InvalidOperationException) {
return false;
}
}
InvalidOperationException
异常,并且您需要检查异常消息以确定异常的根本原因。TryAdd(data)
方法。您还可以传递一个timespan
对象或一个表示超时期限的int
。返回true
或false
。
请注意,如果基础集合类型无法处理重复项,并且您要添加的数据是重复项,则会引发InvalidOperationException
异常。
BlockingCollection<T>
类中的一个错误引起的,这个问题将在.NET 8中得到修复。目前的解决方法是在TryAdd
返回false
时抛出异常。我已经发布了一个包含此解决方法的IProducerConsumerCollection<T>
实现链接. - Theodor Zoulias