2016-09-16 3 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 Framework - это BlockingCollection<T>.

Этот класс позволяет вам блокировать или не блокировать производителей и потребителей. Сторона производителя устанавливается между блокировкой и неблокированием, предоставляя ограничение емкости коллекции в конструкторе. В документации по BlockingCollection<T>.Add(T) методу состояний:

Если ограниченная емкость была указана при инициализации этого экземпляра BlockingCollection<T>, вызов Добавить может блокировать до тех пор, пока пространство доступно для хранения предоставленного товара.

для извлечения элементов, которые можно использовать различные Take и TryTake методы или чрезвычайно удобный BlockingCollection<T>.GetConsumingEnumerable() метод, который создает IEnumerable<T>, что создает IEnumerator<T>, который потребляет один элемент из BlockingCollection<T> при извлечении следующего значения и блокировки в случае, если источник коллекция пуста. То есть до тех пор, пока не вызывается BlockingCollection<T>.CompleteAdding(), и коллекция не принимает никаких новых данных. На данный момент все экземпляры, потребляющие перечисляемые экземпляры будут прекратить блокирование и сообщать о том, что нет никаких данных больше

Таким образом, вы можете в основном реализовать потребителя, как это (как только все остальные данные израсходован.):

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

Такой потребитель может быть запущен в нескольких потоках, чтобы вы могли просматривать несколько потоков из своего источника, если захотите. Вы можете создать любое количество потребляющих счетчиков.

Вы должны знать, что эта коллекция действительно только обертка. Существует конструктор, который позволяет вам установить тип используемой коллекции. По умолчанию ConcurrentQueue<T>.Это означает, что по умолчанию коллекция ведет себя как эта очередь и является коллекцией First-In-First-Out, если вы используете только одного производителя и одного потребителя.


Все, что называется, есть альтернатива. Если вам не нужна блокирующая часть (или вы хотите реализовать блокирующую часть самостоятельно), и если вам не нужен какой-либо порядок элементов внутри вашей коллекции, есть ConcurrentBag<T>. Эта коллекция обрабатывает доступ из нескольких потоков, очень эффективно. Он использует меньшие коллекции внутри оберток ThreadLocal<T>. Таким образом, каждый поток использует свое собственное хранилище, и только если в потоке заканчиваются элементы в собственном хранилище, он начинает получать элементы из другого хранилища потоков.

Использование этой коллекции может быть интересно в случае, если изготовление и потребление происходит последовательно в вашем случае использования. Таким образом, вы сначала добавляете все элементы, и как только это будет сделано, вы будете потреблять все предметы, как с несколькими потоками.

+0

Благодарим вас за обширный ответ, но я принял другого, учитывая, что (A) другой ответ пришел первым, и (B), в конце концов, я переусердствовал с проблемой, и на самом деле проблем не было; иногда самый простой ответ - лучший. –