BlockingCollection(Of T) 的目的是什么
我试图在 .NET 4 上的新并行堆栈上下文中了解 BlockingCollection 的用途。
MSDN 文档说:
BlockingCollection 用作 IProducerConsumerCollection 实例的包装器,允许从集合中进行删除尝试,直到可以删除数据为止。类似地,可以创建 BlockingCollection 以强制 IProducerConsumerCollection 中允许的数据元素数量的上限;然后,对集合的添加尝试可能会被阻止,直到有空间可用于存储添加的项目。
然而,当我查看某些 IProducerConsumerCollection(例如 ConcurrentQueue)的实现时,我发现它们提供了无锁、线程安全的实现。那么为什么需要BlockingCollection提供的锁机制呢? MSDN中的所有示例都显示通过BlockingCollection包装器使用这些集合,直接使用这些集合有什么麻烦?使用 BlockingCollection 会产生什么好处?
I´m trying to understand the purpose of BlockingCollection in the context of the new Parallel Stacks on .NET 4.
The MSDN documentation says:
BlockingCollection is used as a wrapper for an IProducerConsumerCollection instance, allowing removal attempts from the collection to block until data is available to be removed. Similarly, a BlockingCollection can be created to enforce an upper-bound on the number of data elements allowed in the IProducerConsumerCollection; addition attempts to the collection may then block until space is available to store the added items.
However when I look at the implementation of some IProducerConsumerCollection, like ConcurrentQueue I see that they provide a lock free, thread safe, implementations. So why is needed the lock mechanism that BlockingCollection provides? All the examples in the MSDN show using those collections via BlockingCollection wrapper, what are the troubles of using those collections directly? What benefit produces using BlockingCollection?
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(4)
如果您没有其他事情可做(或者更确切地说:在执行操作之前无法继续),则阻塞直到可以执行操作是很方便的。
如果您有一个非阻塞队列要从中读取数据,并且当前没有数据,则必须定期轮询它,或者等待某个信号量,直到有数据为止。如果队列阻塞,这已经自动完成。
同样,如果您尝试添加到已满的非阻塞队列中,操作就会失败,然后您必须弄清楚该怎么做。阻塞队列只会等待,直到有空间。
如果您有一些聪明的做法而不是等待(例如检查另一个队列的数据,或引发 QueueTooFullException),那么您需要非阻塞队列,但通常情况并非如此。
通常,有一种方法可以指定阻塞队列的超时。
Blocking until the operation can be performed is a convenience if you have nothing else to do anyway (or rather: cannot proceed until the operation has been performed).
If you have a non-blocking queue from which you want to read data, and there is no data at the moment, you have to periodically poll it, or wait on some semaphore, until there is data. If the queue blocks, that is already done automatically.
Similarly, if you try to add to a non-blocking queue that is full, the operation will just fail, and then you have to figure out what to do. The blocking queue will just wait until there is space.
If you have something clever to do instead of waiting (such as checking another queue for data, or raising a QueueTooFullException) then you want the non-blocking queue, but often that is not the case.
Often, there is a way to specify a timeout on blocking queues.
锁定的目的就是锁定本身。您可以让多个线程从集合中读取数据,如果没有可用数据,则线程将保持锁定状态,直到新数据到达。
此外,通过设置大小限制,您可以让填充集合的生产者线程尽可能多地向集合中提供数据。当集合达到限制时,线程将锁定,直到消费者线程为数据腾出空间。
这样您就可以使用集合来限制数据的吞吐量,而无需自己进行任何检查。您的线程只需尽可能地读取和写入,并且集合负责根据需要保持线程工作或休眠。
The purpose of locking is the locking itself. You can have several threads read from the collection, and if there is no data available the thread will just stay locked until new data arrives.
Also, with the ability to set a size limit, you can let the producer thread that is filling the collection just feed as much as it can into it. When the collection reaches the limit, the thread will just lock until the consumer threads have made space for the data.
This way you can use the collection to throttle the throughput of data, without doing any checking yourself. Your threads just read and write all they can, and the collection takes care of keeping the threads working or sleeping as needed.
这是一旦你做了就更容易理解的事情之一。
对于生产者消费者,我们有两个对象:生产者和消费者。它们都共享构造时指定的队列,因此它们可以在队列之间写入。
添加生产者消费者非常熟悉,只是 CompleteAdding 有点不同:
消费者似乎没有意义 - 直到您意识到 foreach 不会停止循环,直到队列完成添加。在那之前,如果没有任何物品,它就会继续休眠。由于它是生产者和消费者中集合的相同实例,因此您可以让消费者仅在实际有事情要做时才占用周期,而不必担心停止它、重新启动它等。
因此,您将它们连接在一起通过使用集合。
It's one of those things that's much easier to understand once you do it.
For producer consumer, let's have two objects, Producer and Consumer. They both share a queue they're given when constructed, so they can write between it.
Adding in a producer consumer is pretty familiar, just with the CompleteAdding a little different:
The consumer doesn't seem to make sense - until you realize that the foreach will not stop looping UNTIL the queue has completed adding. Until then, if there's no items, it will just go back to sleep. And since it's the same instance of the collection in the producer and consumer, you can have the consumer ONLY taking up cycles when there's actually things to do, and not have to worry about stopping it, restarting it, etc.
So you wire them together by using the collection.
或者,AsyncEx 提供 AsyncCollection,它是 BlockingCollection 的异步版本。请参阅https://github.com/StephenCleary/AsyncEx/wiki/AsyncCollection
Alternatively, AsyncEx provides AsyncCollection, which is an async version of BlockingCollection. See https://github.com/StephenCleary/AsyncEx/wiki/AsyncCollection