代码之家  ›  专栏  ›  技术社区  ›  Iñaki Elcoro

阻止收集(T)的目的是什么?

  •  18
  • Iñaki Elcoro  · 技术社区  · 15 年前

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

    这个 MSDN 文件上说:

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

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

    4 回复  |  直到 7 年前
        1
  •  17
  •   Thilo    15 年前

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

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

    类似地,如果您试图添加到一个已满的非阻塞队列,那么操作将失败,然后您必须弄清楚该怎么做。阻塞队列将一直等到有空间为止。

    如果您有一些聪明的事情要做,而不是等待(例如检查另一个队列中的数据,或者将队列提升到完全异常),那么您需要非阻塞队列,但通常情况并非如此。

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

        2
  •  7
  •   Guffa    15 年前

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

    此外,有了设置大小限制的能力,您可以让填充集合的生产者线程尽可能多地向其中馈送数据。当集合达到限制时,线程将锁定,直到使用者线程为数据腾出空间。

    通过这种方式,您可以使用收集来限制数据的吞吐量,而不必亲自进行任何检查。您的线程只需要读写它们所能读写的内容,并且集合会根据需要保持线程工作或休眠。

        3
  •  4
  •   Mathieson    9 年前

    一旦你这样做了,你就会更容易理解它。

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

    添加生产者消费者是相当熟悉的,只是添加了一些不同的内容:

        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();
    
        4
  •  0
  •   Matthew K    10 年前

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

    推荐文章