Quelle est la meilleure façon de vérifier si un élément existe dans le 'blockingcollection' avant d'essayer d'en ajouter un nouveau? Fondamentalement, je ne veux pas que les doublons soient ajoutés au BlockingCollection.Vérifier les doublons dans la collection Blocage
Répondre
Utiliser TryAdd(data)
Méthode. Vous pouvez également transmettre un objet timespan
ou int
indiquant un délai d'expiration. Renvoie true
ou false
. Notez que si le type de collection sous-jacent ne peut pas gérer les doublons et que les données que vous essayez d'ajouter sont un doublon, un InvalidOperationException
est déclenché.
Vous devrez implémenter votre propre IProducerConsumerCollection<T>
qui se comporte comme un ensemble (par exemple, aucun doublon n'est autorisé). Voici une version simpliste qui utilise une section critique (C# lock
) pour le rendre thread-safe. Pour les scénarios de forte simultanéité, vous pouvez améliorer les performances en utilisant une classe telle que SpinWait
de la même manière que ConcurrentQueue<T>
.
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();
}
}
Si nécessaire, vous pouvez élaborer sur cette classe pour personnaliser l'égalité en fournissant une option IEqualityComparer<T>
qui est ensuite utilisé pour initialiser le HashSet<T>
. Les méthodes IProducerConsumerCollection<T>.Add
renvoient false
lorsqu'une tentative d'insertion d'un élément en double est effectuée. Il en résulte une InvalidOperationException
lancée par la méthode BlockingCollection<T>.Add
de sorte que vous aurez probablement à envelopper le code pour ajouter un élément dans quelque chose comme ceci:
bool AddItem<T>(BlockingCollection<T> blockingCollection, T item) {
try {
blockingCollection.Add(item);
return true;
}
catch (InvalidOperationException) {
return false;
}
}
Notez que si vous ajoutez des éléments à une collection qui vous a été terminée vous renverront obtenir également un InvalidOperationException
et vous devrez examiner le message d'exception pour déterminer la raison sous-jacente de l'exception.
Toutes les classes sous-jacentes (IProdCons) acceptent les doublons, donc je ne pense pas que cela puisse être utile en soi. –