Потокобезопасные коллекции
В .NET Framework 4 введено пространство имен System.Collections.Concurrent, включающее несколько потокобезопасных и масштабируемых классов коллекций. Несколько потоков могут безопасно и эффективно добавлять и удалять элементы из таких коллекций, не требуя при этом дополнительной синхронизации в пользовательском коде. При написании нового кода пользуйтесь классами параллельных коллекций, когда множество потоков будет вести в коллекцию запись параллельно. Если выполняется только чтение из общей коллекции, вы можете использовать классы в пространстве имен System.Collections.Generic. Мы рекомендуем использовать классы коллекций версии 1.0 только в том случае, если вам нужна среда выполнения .NET Framework до версии 1.1 включительно.
Синхронизация потоков в коллекциях .NET Framework версий 1.0 и 2.0
Коллекции, представленные в .NET Framework 1.0, находятся в пространстве имен System.Collections. Эти коллекции, которые содержат часто используемые классы ArrayList и Hashtable, обеспечивают определенную степень потокобезопасности посредством свойства Synchronized
, которое создает для коллекции потокобезопасную программу-оболочку. Работа программы оболочки заключается в блокировке всей коллекции при каждой операции добавления или удаления. Поэтому каждый поток, который пытается получить доступ к коллекции, должен ждать своей очереди для получения блокировки. Такой подход не является масштабируемым и может привести к значительному снижению производительности для больших коллекций. Также такой подход не защищен полностью от состояния гонки. Дополнительные сведения см. в статье Синхронизация в универсальных коллекциях.
Классы коллекций, представленные в .NET Framework 2.0, находятся в пространстве имен System.Collections.Generic. К ним относятся List<T>, Dictionary<TKey,TValue> и др. Эти классы отличаются улучшенной безопасностью типа и производительностью по сравнению с классами .NET Framework 1.0. Однако классы коллекций .NET Framework 2.0 не обеспечивают синхронизацию потоков. Пользовательский код должен обеспечивать всю синхронизацию при параллельном добавлении элементов в несколько потоков или удалении элементов из них.
Рекомендуем использовать классы многопоточных коллекций .NET Framework 4, так как они обеспечивают не только безопасность типа, как у классов .NET Framework 2.0, но и большую эффективность и более полную потокобезопасность по сравнению с коллекциями .NET Framework 1.0.
Механизм блокировки мелких фрагментов данных и механизм, свободный от блокировки
Некоторые типы многопоточных коллекций используют упрощенные механизмы синхронизации, например SpinLock, SpinWait, SemaphoreSlim и CountdownEvent, которые впервые введены в .NET Framework 4. Эти типы синхронизации обычно используют цикличную работу для коротких промежутков перед помещением потока в фактическое состояние ожидания. При условии, что время ожидания предполагается очень коротким, цикличность требует значительно меньших затрат компьютерных ресурсов, чем ожидание, которое включает в себя переход в режим ядра, требующий больших затрат компьютерных ресурсов. Для классов коллекций, которые используют цикличность, эта эффективность означает, что множество потоков могут добавлять и удалять большое количество элементов. Дополнительные сведения о цикличности и блокировках см. в статья SpinLock и SpinWait.
Классы ConcurrentQueue<T> и ConcurrentStack<T> не используют блокировку. Вместо этого они используют операции класса Interlocked для обеспечения потокобезопасности.
Примечание
Так как классы многопоточных коллекций поддерживают интерфейс ICollection, они включают реализации для свойств IsSynchronized и SyncRoot, даже хотя эти свойства никак не используются. IsSynchronized
всегда возвращает false
, а SyncRoot
всегда имеет значение null
(Nothing
в Visual Basic).
В следующей таблице перечислены типы коллекций в пространстве имен System.Collections.Concurrent.
Type | Описание |
---|---|
BlockingCollection<T> | Предоставляет возможности блокировки и ограничения для всех типов, реализующих интерфейс IProducerConsumerCollection<T>. Дополнительные сведения см. в разделе Общие сведения о коллекции BlockingCollection. |
ConcurrentDictionary<TKey,TValue> | Потокобезопасная реализация словаря пар "ключ-значение". |
ConcurrentQueue<T> | Потокобезопасная реализация очереди с типом "первым поступил — первым обслужен" (FIFO). |
ConcurrentStack<T> | Потокобезопасная реализация стека с типом "последним поступил — первым обслужен" (LIFO). |
ConcurrentBag<T> | Потокобезопасная реализация неупорядоченной коллекции элементов. |
IProducerConsumerCollection<T> | Это интерфейс, тип которого должен быть реализован для использования в классе BlockingCollection . |
См. также
Заголовок | Описание |
---|---|
Общие сведения о коллекции BlockingCollection | Приводится описание функциональных возможностей, которые предоставляются типом BlockingCollection<T>. |
Практическое руководство. Добавление элементов в коллекцию ConcurrentDictionary и их удаление из этой коллекции | Приводится описание добавления и удаления элементов в классе ConcurrentDictionary<TKey,TValue> |
Практическое руководство. Добавление и удаление отдельных элементов коллекции BlockingCollection | Приводится описание порядка добавления и получения элементов из заблокированной коллекции без использования перечислителя, доступного только для чтения. |
Практическое руководство. Добавление функций границы и блокировки в коллекцию | Приводится описание порядка использования всех классов коллекций в качестве базового механизма хранения для коллекции IProducerConsumerCollection<T>. |
Практическое руководство. Использование оператора ForEach для удаления элементов в коллекции BlockingCollection | Описание порядка использования foreach (For Each в Visual Basic) для удаления всех элементов в заблокированной коллекции. |
Практическое руководство. Использование массивов для блокировки коллекций в конвейере | Приводится описание порядка одновременного использования нескольких заблокированных коллекций для реализации конвейера. |
Практическое руководство. Создание пула объектов с помощью класса ConcurrentBag | Показано, как применить параллельный контейнер для повышения производительности в сценариях, где можно повторно использовать объекты вместо постоянного создания новых. |