2016-09-16 31 views
1

所有C#[1] [2]似乎有隐约类似于接口:阻止具有多个元素的集合?对于生产者 - 消费者集合的实现的

private Queue<T> items; 

public void Produce(T item) 
public T Consume() 

在任何实现方式中有像下面?

private Queue<T> items; 

public void Produce(T[] item) 
public T[] Consume(int count) 

希望是这样可以让我一次生产/消费不同数量的物品,而不需要过多的每件物品锁定。这似乎是生产/消耗大量物品的必要条件,但我没有找到任何实现的运气。

[1] C# producer/consumer

[2] Creating a blocking Queue<T> in .NET?

回答

2

希望是,这将让我在一个时间产生/消耗物品的不同数量,而不需要过度的每个项目的锁定。

您可以使用BlockingCollection<T>类;虽然它没有添加或取多个项目的方法,但它不在内部使用锁定。

+0

我做了一个错误的假设,即BlockingCollection '对于很多项目来说会很慢。经过测试,它可以在高吞吐量下正常工作。 –

3

根据你想要实现什么,有多种可能的方法。

IProducerConsumerCollection<T>接口有一些实现。据我所知,.NET框架中唯一的线程安全实现是BlockingCollection<T>

该类允许您拥有阻止或非阻塞的生产者和消费者。生产者端通过在构造函数中为集合提供容量限制来设置阻塞和非阻塞。作为BlockingCollection<T>.Add(T)方法状态的文档:

如果指定了有限的容量时的BlockingCollection<T>这种情况下被初始化,调用添加可能会阻止直到有空间可用于存储所提供的项目。

有关读取的物品可以使用不同TakeTryTake方法或创建一个IEnumerable<T>,创建一个IEnumerator<T>该获取下一个值和在情况下阻断所述源时从BlockingCollection<T>消耗一个元件的非常方便BlockingCollection<T>.GetConsumingEnumerable()方法收集是空的。直到调用BlockingCollection<T>.CompleteAdding()并且该集合不接受任何新数据。此时消耗枚举实例的所有实例都将停止阻止并报告说,没有数据再

所以你基本上可以实现消费者这样的(只要所有剩余的数据已经被消耗掉。):

BlockingCollection<...> bc = ... 
foreach (var item in bc.GetConsumingEnumerable()) 
{ 
    // do something with your item 
} 

这样的消费者可以在多个线程中启动,因此如果您愿意,您可以从源代码读取多个线程。您可以创建任意数量的消耗枚举数。

你应该知道这个集合实际上只是一个包装。有一个构造函数允许你设置使用的集合类型。默认情况下,ConcurrentQueue<T>。这意味着默认情况下,集合的行为与此队列相似,并且是“先进先出”集合,以防您只使用一个生产者和一位消费者。


所有的说法,还有一个选择。如果你不需要阻塞部分(或者你想自己实现阻塞部分),并且如果你不需要集合中的任何元素的顺序,那么有ConcurrentBag<T>。这个集合非常有效地处理来自多个线程的访问。它使用ThreadLocal<T>包装内的较小集合。因此,每个线程都使用它自己的存储,并且只有当线程耗尽自己的存储中的项目时,它才会开始从另一个线程存储中获取项目。

使用这个集合可能会有趣的情况下生产和消费顺序发生在你的用例。因此,您首先添加所有项目,一旦完成,您将消耗所有项目,都使用多个线程。

+0

谢谢你的广泛回答,但是我接受了另一个答案,因为(A)另一个答案是第一个答案,(B)最后,我一直在解决问题,根本没有问题;有时最简单的答案是最好的答案。 –

相关问题