Глава.30 .Гибридные.конструкции.синхронизации.потоков
содержимое коллекции, а значит, в процессе просмотра словаря его вид может
поменяться; об этом следует помнить. Свойство
Count
возвращает количество
элементов в коллекции на момент запроса. Если другие потоки в это время до-
бавляют элементы в коллекцию или извлекают их оттуда, возвращенное значение
может оказаться неверным.
Классы
ConcurrentStack
,
ConcurrentQueue
и
ConcurrentBag
реализуют интер-
фейс
IProducerConsumerCollection
, который выглядит следующим образом:
public interface IProducerConsumerCollection : IEnumerable,
ICollection, IEnumerable {
Boolean TryAdd(T item);
Boolean TryTake(out T item);
T[] ToArray();
void CopyTo(T[] array, Int32 index);
}
Любой реализующий данный интерфейс класс может превратиться в блокирую-
щую коллекцию. Поток, добавляющий элементы, блокируется, если коллекция уже
заполнена, а поток, удаляющий элементы, блокируется, если она пуста. Разумеется,
я по возможности стараюсь избегать таких коллекций, ведь они предназначены
именно для блокировки потоков. Для преобразования коллекции в блокирующую
создается класс
System.Collections.Concurrent.BlockingCollection
, конструк-
тору которого передается ссылка на неблокирующую коллекцию. Этот класс вы-
глядит следующим образом (некоторые методы не показаны):
public class BlockingCollection : IEnumerable, ICollection,
IEnumerable, IDisposable {
public BlockingCollection(
IProducerConsumerCollection collection, Int32 boundedCapacity);
public void Add(T item);
public Boolean TryAdd(
T item, Int32 msTimeout, CancellationToken cancellationToken);
public void CompleteAdding();
public T Take();
public Boolean TryTake(
out T item, Int32 msTimeout, CancellationToken cancellationToken);
public Int32 BoundedCapacity { get; }
public Int32 Count { get; }
public Boolean IsAddingCompleted { get; } // true, если вызван метод
// AddingComplete
public Boolean IsCompleted { get; } // true, если вызван метод
// IsAddingComplete и Count==0
public IEnumerable GetConsumingEnumerable(
CancellationToken cancellationToken);
public void CopyTo(T[] array, int index);
891
Классы.коллекций.для.параллельного.доступа
public T[] ToArray();
public void Dispose();
}
При конструировании экземпляра
BlockingCollection
параметр
bounded-
Capacity
показывает максимально допустимое количество элементов коллекции.
Если поток вызывает метод
Add
для уже заполненной коллекции, он блокируется.
Впрочем, поток может вызвать метод
TryAdd
, передав ему время задержки (в мил-
лисекундах) и/или объект
CancellationToken
. В результате поток блокируется
до добавления элемента, окончания времени ожидания или отмены объекта
CancellationToken
(класс
CancellationToken
подробно рассматривался в главе 27).
Класс
BlockingCollection
реализует интерфейс
IDisposable
. В итоге метод
Dispose
вызывается для внутренней коллекции и удаляет заодно два объекта
SemaphoreSlim
, используемые классом для блокировки потоков-производителей
и потоков-потребителей.
Завершив добавление элементов в коллекцию, поток-производитель должен
вызвать метод
CompleteAdding
. Это даст понять потоку-потребителю, что больше
элементов не будет и цикл
foreach
, использующий объект
GetConsumingEnumerable
,
завершится. Показанный далее код демонстрирует, как организовать сценарий
с участием производителя/потребителя и сигналом о завершении:
public static void Main() {
var bl = new BlockingCollection(new ConcurrentQueue());
// Поток пула получает элементы
ThreadPool.QueueUserWorkItem(ConsumeItems, bl);
// Добавляем в коллекцию 5 элементов
for (Int32 item = 0; item < 5; item++) {
Console.WriteLine("Producing: " + item);
bl.Add(item);
}
// Информируем поток-потребитель, что больше элементов не будет
bl.CompleteAdding();
Console.ReadLine(); // Для целей тестирования
}
private static void ConsumeItems(Object o) {
var bl = (BlockingCollection) o;
// Блокируем до получения элемента, затем обрабатываем его
foreach (var item in bl.GetConsumingEnumerable()) {
Console.WriteLine("Consuming: " + item);
}
// Коллекция пуста и там больше не будет элементов
Console.WriteLine("All items have been consumed");
}
892
Do'stlaringiz bilan baham: |