BlockingCollection(Of T)的目的是什么

时间:2009-12-21 07:57:49

标签: .net parallel-extensions

我试图在.NET 4上的新并行堆栈的上下文中理解BlockingCollection的目的。

MSDN文档说:

  

BlockingCollection用作IProducerConsumerCollection实例的包装器,允许从集合中删除尝试以阻止数据可用于删除。类似地,可以创建BlockingCollection以强制执行IProducerConsumerCollection中允许的数据元素数量的上限;然后可以阻止对集合的添加尝试,直到空间可用于存储添加的项目。

但是当我查看一些IProducerConsumerCollection的实现时,比如ConcurrentQueue,我看到它们提供了一个无锁,线程安全的实现。那么为什么需要BlockingCollection提供的锁机制呢? MSDN中的所有示例都显示通过BlockingCollection包装器使用这些集合,直接使用这些集合有哪些麻烦?使用BlockingCollection会产生什么好处?

4 个答案:

答案 0 :(得分:18)

阻止直到可以执行操作是一种方便,如果您无其他任何事情要做(或者更确切地说:在执行操作之前无法继续)。

如果你有一个非阻塞队列,你想从中读取数据,并且目前没有数据,你必须定期轮询它,或等待一些信号量,直到有数据。如果队列阻塞,那就已经自动完成了。

同样,如果您尝试添加到已满的非阻塞队列,则操作将失败,然后您必须弄清楚要执行的操作。阻塞队列只会等到有空间。

如果您有一些聪明的事情而不是等待(例如检查另一个队列的数据,或者引发QueueTooFullException),那么您需要非阻塞队列,但通常情况并非如此。

通常,有一种方法可以指定阻塞队列的超时。

答案 1 :(得分:7)

锁定的目的是锁定本身。您可以从集合中读取多个线程,如果没有可用数据,则线程将保持锁定状态,直到新数据到达。

此外,通过设置大小限制的功能,您可以让填充集合的生产者线程尽可能多地添加到其中。当集合达到限制时,线程将直接锁定,直到消费者线程为数据腾出空间。

通过这种方式,您可以使用该集合来限制数据的吞吐量,而无需自行检查。你的线程只是随便读写,并且集合负责根据需要保持线程正常工作或休眠。

答案 2 :(得分:4)

这是你做过之后更容易理解的事情之一。

对于生产者消费者,让我们有两个对象,生产者和消费者。它们都共享一个它们在构造时给出的队列,因此它们可以在它之间写入。

添加一个生产者消费者是非常熟悉的,只需要与CompleteAdding略有不同:

    public class Producer{
       private BlockingCollection<string> _queue;
       public Producer(BlockingCollection<string> queue){_queue = queue;}  

       //a method to do something
       public MakeStuff()
       {
           for(var i=0;i<Int.MaxValue;i++)
           {
                _queue.Add("a string!");
           }

           _queue.CompleteAdding();
       }
}

消费者似乎没有意义 - 直到你意识到foreach不会停止循环UNTIL队列已完成添加。在那之前,如果没有物品,它就会重新入睡。而且由于它与生产者和消费者中的集合实例相同,因此您可以让消费者在实际操作时只占用周期,而不必担心停止它,重新启动它等等。

public class Consumer()
{
      private BlockingCollection<string> _queue;
      public Consumer(BlockingCollection<string> queue)
      {
           _queue = queue;
      }

      public void WriteStuffToFile()
      {
          //we'll hold until our queue is done.  If we get stuff in the queue, we'll start processing it then
          foreach(var s in _queue.GetConsumingEnumerable())
          {
             WriteToFile(s);
          }
      }
}

因此,您可以使用该集合将它们连接在一起。

var queue = new BlockingCollection<string>();
var producer = new Producer(queue);
var consumer = new Consumer(queue);

producer.MakeStuff();
consumer.WriteStuffToFile();

答案 3 :(得分:0)

或者,AsyncEx提供AsyncCollection,它是BlockingCollection的异步版本。见https://github.com/StephenCleary/AsyncEx/wiki/AsyncCollection