19

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

MSDN文档说:

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

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

4

4 回答 4

18

如果您无论如何都无事可做(或者更确切地说:在执行操作之前无法继续),那么阻塞直到可以执行操作是一种方便。

如果您有一个要从中读取数据的非阻塞队列,而此时没有数据,则必须定期轮询它,或者等待某个信号量,直到有数据为止。如果队列阻塞,那已经自动完成了。

同样,如果你试图添加到一个已满的非阻塞队列中,操作就会失败,然后你必须弄清楚该怎么做。阻塞队列将一直等到有空间为止。

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

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

于 2009-12-21T08:20:16.207 回答
7

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

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

这样,您可以使用集合来限制数据的吞吐量,而无需自己进行任何检查。您的线程只是尽可能地读取和写入,并且该集合负责根据需要保持线程工作或休眠。

于 2009-12-21T08:21:25.813 回答
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 不会停止循环,直到队列完成添加。在那之前,如果没有物品,它就会重新进入睡眠状态。而且由于它是生产者和消费者中集合的同一个实例,因此您可以让消费者仅在有实际事情要做时才占用周期,而不必担心停止它、重新启动它等。

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();
于 2013-11-07T22:35:01.830 回答
0

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

于 2015-01-20T04:58:27.723 回答