Глава.30 .Гибридные.конструкции.синхронизации.потоков
мени, поэтому блокировка заведомо будет удерживаться в течение очень короткого
промежутка.
Аналогичным образом метод
Release
ограничивается целочисленными вы-
числениями, сравнением и, возможно, выведением объекта
TaskCompletionSource
из очереди или его созданием. Все это тоже происходит очень быстро. Все это по-
зволило мне с уверенностью использовать
SpinLock
для защиты доступа к
Queue
.
Выполнение потоков никогда не блокируется, что способствует написанию мас-
штабируемого, быстрого кода.
Классы коллекций
для параллельного доступа
В FCL существует четыре безопасных в отношении потоков класса коллекций, при-
надлежащих пространству имен
System.Collections.Concurrent
:
ConcurrentQueue
,
ConcurrentStack
,
ConcurrentDictionary
и
ConcurrentBag
. Вот как выглядят наи-
более часто используемые члены:
// Обработка элементов по алгоритму FIFO
public class ConcurrentQueue : IProducerConsumerCollection,
IEnumerable, ICollection, IEnumerable {
public ConcurrentQueue();
public void Enqueue(T item);
public Boolean TryDequeue(out T result);
public Int32 Count { get; }
public IEnumerator GetEnumerator();
}
// Обработка элементов по алгоритму LIFO
public class ConcurrentStack : IProducerConsumerCollection,
IEnumerable, ICollection, IEnumerable {
public ConcurrentStack();
public void Push(T item);
public Boolean TryPop(out T result);
public Int32 Count { get; }
public IEnumerator GetEnumerator();
}
// Несортированный набор элементов с возможностью хранения дубликатов
public class ConcurrentBag : IProducerConsumerCollection,
IEnumerable, ICollection, IEnumerable {
public ConcurrentBag();
public void Add(T item);
public Boolean TryTake(out T result);
889
Классы.коллекций.для.параллельного.доступа
public Int32 Count { get; }
public IEnumerator GetEnumerator();
}
// Несортированный набор пар ключ/значение
public class ConcurrentDictionary : IDictionary,
ICollection>, IEnumerableTValue>>, IDictionary, ICollection, IEnumerable {
public ConcurrentDictionary();
public Boolean TryAdd(TKey key, TValue value);
public Boolean TryGetValue(TKey key, out TValue value);
public TValue this[TKey key] { get; set; }
public Boolean TryUpdate(
TKey key, TValue newValue, TValue comparisonValue);
public Boolean TryRemove(TKey key, out TValue value);
public TValue AddOrUpdate(
TKey key, TValue addValue, Func updateValueFactory);
public TValue GetOrAdd(TKey key, TValue value);
public Int32 Count { get; }
public IEnumerator> GetEnumerator();
}
Эти классы коллекций являются неблокирующими. При попытке извлечь несу-
ществующий элемент поток немедленно возвращает управление, а не блокируется,
ожидая появления элемента. Именно поэтому такие методы, как
TryDequeue
,
TryPop
,
TryTake
и
TryGetValue
, при получении элемента возвращают значение
true
, а при
его невозможности —
false
.
Хотя эти коллекции являются неблокирующими, это вовсе не означает, что они
обходятся без синхронизации. Класс
ConcurrentDictionary
внутренне использует
класс
Monitor
, но блокировка удерживается только на короткое время, необходи-
мое для работы с элементом коллекции. В то же время классы
ConcurrentQueue
и
ConcurrentStack
для манипулирования коллекцией используют методы
Interlocked
и поэтому обходятся вообще без блокирования. Один объект
ConcurrentBag
внутрен-
не состоит из объекта мини-коллекций для каждого потока. При добавлении нового
элемента методы
Interlocked
помещают его в мини-коллекцию вызывающего потока.
При попытке извлечь элемент его наличие опять же проверяется в мини-коллекции
вызывающего потока. При обнаружении элемента задействуется метод класса
Interlocked
. Если же элемент в рассматриваемой мини-коллекции отсутствует, ме-
тоды класса
Monitor
извлекают его из мини-коллекции другого потока. Мы говорим,
что имеет место
захват
(stealing) элемента у другого потока.
Обратите внимание, что все рассматриваемые классы обладают методом
GetEnumerator
, обычно используемым в инструкции C#
foreach
, но допустимым
и в языке LINQ. Для классов
ConcurrentStack
,
ConcurrentQueue
и
ConcurrentBag
метод
GetEnumerator
создает снимок содержимого коллекции и возвращает за-
фиксированные элементы; при этом реальное содержимое коллекции уже может
измениться. Метод
GetEnumerator
класса
ConcurrentDictionary
не фиксирует
890
Do'stlaringiz bilan baham: |