how to access the underlying default concurrent queue of a blocking collection

后端 未结 3 1251
广开言路
广开言路 2020-12-11 03:05

I have multiple producers and a single consumer. However if there is something in the queue that is not yet consumed a producer should not queue it again. (unique no duplica

3条回答
  •  不思量自难忘°
    2020-12-11 03:58

    This is an interesting question. This is the first time I have seen someone ask for a blocking queue that ignores duplicates. Oddly enough I could find nothing like what you want that already exists in the BCL. I say this is odd because BlockingCollection can accept a IProducerConsumerCollection as the underlying collection which has the TryAdd method that is advertised as being able to fail when duplicates are detected. The problem is that I see no concrete implementation of IProducerConsumerCollection that prevents duplicates. At least we can write our own.

    public class NoDuplicatesConcurrentQueue : IProducerConsumerCollection
    {
      // TODO: You will need to fully implement IProducerConsumerCollection.
    
      private Queue queue = new Queue();
    
      public bool TryAdd(T item)
      {
        lock (queue)
        {
          if (!queue.Contains(item))
          {
            queue.Enqueue(item);
            return true;
          }
          return false;
        }
      }
    
      public bool TryTake(out T item)
      {
        lock (queue)
        {
          item = null;
          if (queue.Count > 0)
          {
            item = queue.Dequeue();
          }
          return item != null;
        }
      }
    }
    

    Now that we have our IProducerConsumerCollection that does not accept duplicates we can use it like this:

    public class Example
    {
      private BlockingCollection queue = new BlockingCollection(new NoDuplicatesConcurrentQueue());
    
      public Example()
      {
        new Thread(Consume).Start();
      }
    
      public void Produce(object item)
      {
        bool unique = queue.TryAdd(item);
      }
    
      private void Consume()
      {
        while (true)
        {
          object item = queue.Take();
        }
      }
    }
    
    
    

    You may not like my implementation of NoDuplicatesConcurrentQueue. You are certainly free to implement your own using ConcurrentQueue or whatever if you think you need the low-lock performance that the TPL collections provide.

    Update:

    I was able to test the code this morning. There is some good news and bad news. The good news is that this will technically work. The bad news is that you probably will not want to do this because BlockingCollection.TryAdd intercepts the return value from the underlying IProducerConsumerCollection.TryAdd method and throws an exception when false is detected. Yep, that is right. It does not return false like you would expect and instead generates an exception. I have to be honest, this is both surprising and ridiculous. The whole point of the TryXXX methods is that they should not throw exceptions. I am deeply disappointed.

    提交回复
    热议问题