← Назад к вопросам

Какие знаешь потокобезопасные коллекции?

2.0 Middle🔥 202 комментариев
#Основы C# и .NET

Комментарии (2)

🐱
deepseek-v3.2PrepBro AI6 апр. 2026 г.(ред.)

Ответ сгенерирован нейросетью и может содержать ошибки

Потокобезопасные коллекции в C#

В C# потокобезопасные коллекции предоставляются в основном через пространство имен System.Collections.Concurrent, введенное в .NET Framework 4.0. Эти коллекции предназначены для безопасного использования в многопоточных сценариях без необходимости ручной синхронизации с помощью lock или других примитивов синхронизации. Вот основные из них:

Основные коллекции из System.Collections.Concurrent

1. ConcurrentQueue<T>

  • Потокобезопасная реализация очереди FIFO (First-In-First-Out).
  • Основные методы: Enqueue (добавление), TryDequeue (извлечение), TryPeek.
  • Не блокирующая операция чтения при пустой очереди.
using System.Collections.Concurrent;

var queue = new ConcurrentQueue<int>();
queue.Enqueue(1);
if (queue.TryDequeue(out int result))
{
    Console.WriteLine($"Извлечено: {result}");
}

2. ConcurrentStack<T>

  • Потокобезопасная реализация стека LIFO (Last-In-First-Out).
  • Основные методы: Push (добавление), TryPop (извлечение), TryPeek.
var stack = new ConcurrentStack<int>();
stack.Push(1);
if (stack.TryPop(out int result))
{
    Console.WriteLine($"Извлечено: {result}");
}

3. ConcurrentBag<T>

  • Неупорядоченная коллекция, оптимизированная для сценариев, где один поток добавляет и извлекает элементы.
  • Эффективна, когда один поток является и производителем, и потребителем.
  • Основные методы: Add, TryTake, TryPeek.
var bag = new ConcurrentBag<int>();
bag.Add(1);
if (bag.TryTake(out int result))
{
    Console.WriteLine($"Извлечено: {result}");
}

4. ConcurrentDictionary<TKey, TValue>

  • Потокобезопасная реализация словаря.
  • Поддерживает атомарные операции, такие как AddOrUpdate, GetOrAdd.
var dict = new ConcurrentDictionary<string, int>();
dict["key"] = 1;
int value = dict.AddOrUpdate("key", 2, (k, old) => old + 1);

5. BlockingCollection<T>

  • Обертка над потокобезопасной коллекцией, реализующая шаблон Producer-Consumer.
  • Поддерживает ограничение емкости и блокирующие операции.
  • Может использовать любую коллекцию, реализующую IProducerConsumerCollection<T> (например, ConcurrentQueue<T> по умолчанию).
var blockingCollection = new BlockingCollection<int>(boundedCapacity: 5);
// Producer
Task.Run(() => { blockingCollection.Add(1); });
// Consumer
Task.Run(() => 
{
    foreach (var item in blockingCollection.GetConsumingEnumerable())
    {
        Console.WriteLine($"Обработано: {item}");
    }
});

Другие потокобезопасные коллекции и структуры

6. Immutable Collections (System.Collections.Immutable)

  • Неизменяемые коллекции, которые безопасны для чтения из нескольких потоков без блокировок.
  • Любая "модификация" создает новую коллекцию, что делает их подходящими для функционального программирования.
  • Примеры: ImmutableList<T>, ImmutableDictionary<TKey, TValue>, ImmutableQueue<T>.
using System.Collections.Immutable;

var immutableList = ImmutableList<int>.Empty;
var newList = immutableList.Add(1).Add(2); // Создает новую коллекцию

7. Channels (System.Threading.Channels)

  • Высокопроизводительная библиотека для асинхронного обмена данными между производителями и потребителями.
  • Поддерживает асинхронные операции чтения/записи.
using System.Threading.Channels;

var channel = Channel.CreateUnbounded<int>();
// Writer
await channel.Writer.WriteAsync(1);
// Reader
while (await channel.Reader.WaitToReadAsync())
{
    if (channel.Reader.TryRead(out int item))
    {
        Console.WriteLine($"Прочитано: {item}");
    }
}

Ключевые особенности и рекомендации

  • Атомарность операций: Методы TryDequeue, TryPop, TryTake и другие "Try"-методы обеспечивают атомарность, что критично для потокобезопасности.
  • Производительность: Коллекции Concurrent используют легковесные механизмы синхронизации (spin locks, CAS операции) вместо тяжелых мьютексов, что повышает производительность в высоконагруженных сценариях.
  • Сценарии использования:
    • ConcurrentDictionary — идеален для кэшей или разделяемых словарей.
    • BlockingCollection — для шаблона Producer-Consumer с ограниченной емкостью.
    • ConcurrentQueue — для параллельной обработки задач.
    • Immutable Collections — когда данные чаще читаются, чем изменяются.
  • Ограничения: Некоторые операции, такие как Count, могут быть дорогостоящими и не всегда отражают актуальное состояние из-за параллельных модификаций.

Пример использования в реальном сценарии

using System.Collections.Concurrent;
using System.Threading.Tasks;

// Пример параллельной обработки данных с ConcurrentQueue
var sourceQueue = new ConcurrentQueue<int>(Enumerable.Range(1, 100));
var results = new ConcurrentBag<int>();

Parallel.ForEach(Partitioner.Create(0, 100), range =>
{
    for (int i = range.Item1; i < range.Item2; i++)
    {
        if (sourceQueue.TryDequeue(out int item))
        {
            results.Add(item * 2); // Обработка данных
        }
    }
});

Console.WriteLine($"Обработано элементов: {results.Count}");

Использование потокобезопасных коллекций упрощает разработку многопоточных приложений, снижая риск дедлоков и состояний гонки. Однако важно выбирать коллекцию, соответствующую конкретному сценарию, чтобы достичь оптимальной производительности.