Какова цель BlockingCollection (of T)
Im пытается понять цель BlockingCollection в контексте новых параллельных стеков на .NET 4.
на MSDN документация говорит:
BlockingCollection используется в качестве оболочки для экземпляра IProducerConsumerCollection, что позволяет блокировать попытки удаления из коллекции до тех пор, пока данные не будут доступны для удаления. Аналогично, BlockingCollection может быть создан для обеспечения верхней границы числа элементов данных разрешено в IProducerConsumerCollection; попытки добавления в коллекцию могут затем блокировать, пока не будет доступно пространство для хранения добавленных элементов.
однако, когда я смотрю на реализацию некоторого IProducerConsumerCollection, например ConcurrentQueue, я вижу, что они предоставляют свободную от блокировки, потокобезопасную реализацию. Так зачем нужен механизм блокировки, который предоставляет BlockingCollection? Все примеры в MSDN показывают использование этих коллекций через BlockingCollection wrapper, в чем проблемы использования этих коллекций напрямую? Какую выгоду производит с использованием методом blockingcollection?
4 ответов
блокировка до тех пор, пока операция не может быть выполнена, является удобством, если вам все равно нечего делать (или, скорее: не может продолжаться, пока операция не будет выполнена).
Если у вас есть неблокирующая очередь, из которой вы хотите читать данные, и на данный момент нет данных, вы должны периодически опрашивать ее или ждать на каком-либо семафоре, пока не появятся данные. Если очередь блокируется, это уже делается автоматически.
аналогично, если вы пытаетесь добавить к неблокирующая очередь, которая заполнена, операция просто завершится неудачей, и тогда вам придется выяснить, что делать. Очередь блокировки будет просто ждать, пока не останется места.
Если у вас есть что-то умное, чтобы сделать вместо ожидания (например, проверка другой очереди для данных или создание QueueTooFullException), то вы хотите неблокирующую очередь, но часто это не так.
часто существует способ указать тайм-аут для блокировки очередей.
цель замок-это сам замок. Вы можете прочитать несколько потоков из коллекции, и если нет доступных данных, поток будет заблокирован до поступления новых данных.
кроме того, с возможностью установить ограничение размера, вы можете позволить потоку производителя, который заполняет коллекцию, просто кормить столько, сколько он может в него. Когда коллекция достигает предела, поток просто блокируется, пока потоки-потребители не освободят место для данные.
таким образом, вы можете использовать коллекцию для дросселирования пропускной способности данных, не проверяя себя. Ваши потоки просто читают и пишут все, что могут, и коллекция заботится о том, чтобы потоки работали или спали по мере необходимости.
Это одна из тех вещей, которые гораздо легче понять, как только вы это сделаете.
для потребителя-производителя, давайте иметь два объекта, производителя и потребителя. Они оба разделяют очередь, которую им дают при построении, поэтому они могут писать между ней.
добавление в потребитель производителя довольно знакомо, просто с 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();
кроме того, AsyncEx предоставляет AsyncCollection, который является асинхронной версией BlockingCollection. См.https://github.com/StephenCleary/AsyncEx/wiki/AsyncCollection