What is the best way to check if an item is existed in the BlockingCollection
before trying to add a new one? Basically I do not want duplicates to be added to the BlockingCollection
.

- 34,835
- 7
- 69
- 104

- 157
- 1
- 14
2 Answers
You will have to implement your own IProducerConsumerCollection<T>
that behaves like a set (e.g. no duplicates allowed). Here is a simplistic version that use a critical section (C# lock
) to make it thread-safe. For high concurrency scenarios you may be able to improve performance by using a class like SpinWait
the same way as ConcurrentQueue<T>
does.
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();
}
}
If required you can elaborate on this class to customize equality by supplying an optional IEqualityComparer<T>
that then is used to initialize the HashSet<T>
.
The IProducerConsumerCollection<T>.Add
methods returns false
when there is an attempt to insert a duplicate item. This results in an InvalidOperationException
thrown by the BlockingCollection<T>.Add
method so you will probably have to wrap the code to add an item into something like this:
bool AddItem<T>(BlockingCollection<T> blockingCollection, T item) {
try {
blockingCollection.Add(item);
return true;
}
catch (InvalidOperationException) {
return false;
}
}
Note that if you add items to a collection that has been completed you will also get an InvalidOperationException
and you will have to examine the exception message to determine the underlying reason for the exception.

- 104,481
- 22
- 209
- 256
-
1I found an issue with this approach, the blocking collection does not reach the upper bound if duplicate items are added by producers that result in InvalidOperationException – Aashish Upadhyay Jun 02 '18 at 15:05
-
@AashishUpadhyay the issue is caused by [a bug](https://github.com/dotnet/runtime/issues/69816) in the `BlockingCollection
` class, that is going to be fixed in .NET 8. For now the workaround is to throw an exception instead of returning `false` from the `TryAdd`. I have posted an `IProducerConsumerCollection – Theodor Zoulias Feb 05 '23 at 22:56` implementation that incorporates this workaround [here](https://stackoverflow.com/questions/7652669/concurrent-collections-and-unique-elements/75354636#75354636 "Concurrent collections and unique elements").
Use TryAdd(data)
Method. You can also pass in a timespan
object or and int
indicating a timeout period. Returns true
or false
.
Note that if the underlying collection type cannot handle duplicates and the data you are trying to add IS a duplicate then an InvalidOperationException
is raised.

- 1,478
- 2
- 10
- 8
-
2All underlying (IProdCons) classes accept duplicates, so I don't think this will help by itself. – H H Mar 02 '16 at 11:40