← Назад к вопросам
Какие знаешь потокобезопасные коллекции?
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}");
Использование потокобезопасных коллекций упрощает разработку многопоточных приложений, снижая риск дедлоков и состояний гонки. Однако важно выбирать коллекцию, соответствующую конкретному сценарию, чтобы достичь оптимальной производительности.