Примечание.
Для доступа к этой странице требуется авторизация. Вы можете попробовать войти или изменить каталоги.
Для доступа к этой странице требуется авторизация. Вы можете попробовать изменить каталоги.
BlockingCollection<T> — это потокобезопасный класс коллекций, который предоставляет следующие возможности.
Реализация шаблона Producer-Consumer.
Параллельное добавление и получение элементов из нескольких потоков.
Максимальная необязательная вместимость.
Операции вставки и удаления, которые блокируются, если коллекция пуста или полна.
Операции вставки и удаления, которые пытаются не блокироваться или блокируются до указанного периода времени.
Инкапсулирует любой тип коллекции, реализующий IProducerConsumerCollection<T>
Отмена с токенами отмены.
Два типа перечисления с
foreach(For Eachв Visual Basic):Перечисление, доступное только для чтения.
Перечисление, которое удаляет элементы по мере их перечисления.
Ограничение и блокировка поддержки
BlockingCollection<T> поддерживает привязку и блокировку. Ограничение означает, что можно установить максимальную емкость коллекции. Ограничение важно в определённых сценариях, так как оно позволяет управлять максимальным размером коллекции в памяти и предотвращает порождающие потоки от слишком сильного опережения потребляющих потоков.
Несколько потоков или задач могут одновременно добавлять элементы в коллекцию, и если коллекция достигает указанной максимальной емкости, потоки, добавляющие элементы, будут заблокированы, пока элемент не будет удален. Несколько потребителей могут одновременно удалять элементы, и если коллекция становится пустой, потребляемые потоки блокируются до тех пор, пока производитель не добавит элемент. Порождающий поток может вызвать CompleteAdding, чтобы указать, что больше элементов добавляться не будет. Потребители отслеживают IsCompleted свойство, чтобы узнать, когда коллекция пуста, и больше элементов не будет добавлено. В следующем примере показан простой BlockingCollection с ограниченной емкостью 100. Задача производителя добавляет элементы в коллекцию до тех пор, пока действуют некоторые внешние условия, и затем вызывает CompleteAdding. Задача потребителя принимает элементы, пока свойство IsCompleted истинно.
// A bounded collection. It can hold no more
// than 100 items at once.
BlockingCollection<Data> dataItems = new BlockingCollection<Data>(100);
// A simple blocking consumer with no cancellation.
Task.Run(() =>
{
while (!dataItems.IsCompleted)
{
Data data = null;
// Blocks if dataItems.Count == 0.
// IOE means that Take() was called on a completed collection.
// Some other thread can call CompleteAdding after we pass the
// IsCompleted check but before we call Take.
// In this example, we can simply catch the exception since the
// loop will break on the next iteration.
try
{
data = dataItems.Take();
}
catch (InvalidOperationException) { }
if (data != null)
{
Process(data);
}
}
Console.WriteLine("\r\nNo more items to take.");
});
// A simple blocking producer with no cancellation.
Task.Run(() =>
{
while (moreItemsToAdd)
{
Data data = GetData();
// Blocks if numbers.Count == dataItems.BoundedCapacity
dataItems.Add(data);
}
// Let consumer know we are done.
dataItems.CompleteAdding();
});
' A bounded collection. It can hold no more
' than 100 items at once.
Dim dataItems = New BlockingCollection(Of Data)(100)
' A simple blocking consumer with no cancellation.
Task.Factory.StartNew(Sub()
While dataItems.IsCompleted = False
Dim dataItem As Data = Nothing
Try
dataItem = dataItems.Take()
Catch e As InvalidOperationException
' IOE means that Take() was called on a completed collection.
' In this example, we can simply catch the exception since the
' loop will break on the next iteration.
End Try
If (dataItem IsNot Nothing) Then
Process(dataItem)
End If
End While
Console.WriteLine(vbCrLf & "No more items to take.")
End Sub)
' A simple blocking producer with no cancellation.
Task.Factory.StartNew(Sub()
While moreItemsToAdd = True
Dim item As Data = GetData()
' Blocks if dataItems.Count = dataItems.BoundedCapacity.
dataItems.Add(item)
End While
' Let consumer know we are done.
dataItems.CompleteAdding()
End Sub)
Полный пример см. как: Как добавить и извлечь элементы поштучно из BlockingCollection.
Операции блокировки по времени
При повременной блокировке TryAdd и TryTake операциях с привязанными коллекциями метод пытается добавить или принять элемент. Если элемент доступен, он помещается в переменную, переданную по ссылке, и метод возвращает значение true. Если элемент не извлекается после указанного периода ожидания, метод возвращает значение false. Затем поток свободен для выполнения некоторых других полезных действий, прежде чем повторить попытку доступа к коллекции. Для примера доступа с временной блокировкой см. второй пример в статье "Практическое руководство: добавление и извлечение элементов по отдельности из BlockingCollection".
Отмена операций добавления и удаления
Операции добавления и принятия обычно выполняются в цикле. Вы можете отменить цикл, передав CancellationToken в метод TryAdd или TryTake, а затем, при каждом повторении, проверяя значение свойства токена IsCancellationRequested. Если значение истинно, то вы можете сами решить, как ответить на запрос отмены, очистив любые ресурсы и выйдя из цикла. В следующем примере показана перегрузка метода TryAdd, который принимает маркер отмены, и код метода, который его использует.
do
{
// Cancellation causes OCE. We know how to handle it.
try
{
success = bc.TryAdd(itemToAdd, 2, ct);
}
catch (OperationCanceledException)
{
bc.CompleteAdding();
break;
}
//...
} while (moreItems == true);
Do While moreItems = True
' Cancellation causes OCE. We know how to handle it.
Try
success = bc.TryAdd(itemToAdd, 2, ct)
Catch ex As OperationCanceledException
bc.CompleteAdding()
Exit Do
End Try
Loop
Пример добавления поддержки отмены см. во втором примере в разделе "Практическое руководство. Добавление и использование элементов по отдельности из объекта BlockingCollection".
Указание типа коллекции
При создании BlockingCollection<T>можно указать не только ограниченную емкость, но и тип используемой коллекции. Например, можно указать ConcurrentQueue<T> для поведения "первым пришёл — первым вышел" (FIFO) или ConcurrentStack<T> для поведения "последним пришёл — первым вышел" (LIFO). Вы можете использовать любой класс коллекции, реализующий IProducerConsumerCollection<T> интерфейс. Тип коллекции по умолчанию для BlockingCollection<T> это ConcurrentQueue<T>. В следующем примере кода показано, как создать BlockingCollection<T> для строк с емкостью 1000 и использовать ConcurrentBag<T>.
Dim bc = New BlockingCollection(Of String)(New ConcurrentBag(Of String()), 1000)
BlockingCollection<string> bc = new BlockingCollection<string>(new ConcurrentBag<string>(), 1000 );
Дополнительные сведения см. в разделе "Практическое руководство. Добавление ограничивающих и блокирующих функций в коллекцию".
Поддержка IEnumerable
BlockingCollection<T>
GetConsumingEnumerable предоставляет метод, позволяющий потребителям использовать foreach (For Eachв Visual Basic) удалять элементы до завершения коллекции, что означает, что она пуста и больше элементов не будет добавлена. Для получения дополнительной информации см. как использовать ForEach для удаления элементов в BlockingCollection.
Использование нескольких BlockingCollections как одной единицы
В сценариях, в которых потребитель должен одновременно принимать элементы из нескольких коллекций, можно создавать массивы и использовать статические методы BlockingCollection<T> , такие как TakeFromAny и AddToAny которые будут добавляться или приниматься из любой коллекции в массиве. Если одна коллекция блокируется, метод немедленно пытается обратиться к другой, пока не найдет ту, которая может выполнить операцию. Дополнительные сведения см. в статье "Практическое руководство. Использование массивов блокирующих коллекций в конвейере".