检查阻止集合中的重复项 [英] Check for duplicates in Blocking collection

查看:60
本文介绍了检查阻止集合中的重复项的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

在尝试添加新项目之前检查"blockingcollection"中是否存在某项的最佳方法是什么?基本上,我不希望将重复项添加到BlockingCollection中.

解决方案

您将必须实现自己的 IProducerConsumerCollection< T> ,其行为类似于集合(例如,不允许重复).这是一个简化的版本,它使用关键部分(C# lock )使其具有线程安全性.对于高并发情况,您可以使用与 ConcurrentQueue< T> 相同的方法,使用 SpinWait 之类的类来提高性能.

 公共类ProducerConsumerSet< T>:IProducerConsumerCollection< T>{只读对象gate = new object();只读队列< T>queue = new Queue< T>();只读HashSet< T>hashSet = new HashSet< T>();public void CopyTo(T [] array,int index){如果(数组== null)抛出新的ArgumentNullException("array");如果(索引< 0)抛出新的ArgumentOutOfRangeException("index");锁(门)queue.CopyTo(array,index);}公共布尔TryAdd(T项目){锁(门){如果(hashSet.Contains(item))返回false;queue.Enqueue(item);hashSet.Add(item);返回true;}}公共布尔TryTake(T项除外){锁(门){如果(queue.Count == 0){item = default(T);返回false;}item = queue.Dequeue();hashSet.Remove(item);返回true;}}公共T [] ToArray(){锁(门)返回queue.ToArray();}public void CopyTo(Array array,int index){如果(数组== null)抛出新的ArgumentNullException("array");锁(门)((ICollection)队列).CopyTo(数组,索引);}public int Count {得到{return queue.Count;}}公共对象SyncRoot {得到{回程门;}}公共布尔IsSynchronized {得到{return true;}}公共IEnumerator< T>GetEnumerator(){列表< T>list = null;锁(门)list = queue.ToList();返回list.GetEnumerator();}IEnumerator IEnumerable.GetEnumerator(){返回GetEnumerator();}} 

如果需要,您可以提供一个可选的 IEqualityComparer< T> ,然后用于初始化 HashSet< T> ,来详细说明此类以自定义相等性.

IProducerConsumerCollection< T> .Add 方法在尝试插入重复项时返回 false .这会导致 BlockingCollection< T> .Add 方法抛出 InvalidOperationException ,因此您可能必须包装代码才能将项目添加到类似这样的内容中:

  bool AddItem< T>(BlockingCollection< T> blockingCollection,T item){尝试 {blockingCollection.Add(item);返回true;}catch(InvalidOperationException){返回false;}} 

请注意,如果将项目添加到已完成的集合中,您还将获得 InvalidOperationException ,并且您将必须检查异常消息以确定异常的根本原因.

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.

解决方案

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.

这篇关于检查阻止集合中的重复项的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆