TGViewer
.NET Разработчик .NET Разработчик @netdeveloperdiary · 6.76K subscribers
Post #1500 1.44K
День 1212. #ЗаметкиНаПолях #AsyncTips
Потокобезопасные словари

Задача
Имеется коллекция «ключ/значение» (например, кэш в памяти), которая должна поддерживаться в синхронизированном состоянии, даже если несколько потоков выполняют с ней операции чтения и записи.

Решение
Тип ConcurrentDictionary<TKey, TValue> является потокобезопасным гарантирует быстрый доступ в подавляющем большинстве сценариев. Его API сильно отличается от стандартного типа Dictionary<TKey, TValue>, поскольку он должен иметь дело с конкурентным доступом из многих потоков.

Запись
Чтобы задать значение для ключа, используйте метод AddOrUpdate:
var d = new ConcurrentDictionary<int, string>();
string newValue = d.AddOrUpdate(
0,
key => "Zero",
(key, oldValue) => "Zero"
);

Метод AddOrUpdate выглядит сложно, так как должен делать несколько вещей в зависимости от текущего содержимого словаря.
Аргументы:
- ключ,
- делегат, преобразующий ключ (в данном случае 0) в значение, которое будет добавлено в словарь (в данном случае "Zero"). Вызывается, только если ключа не существует в словаре.
- делегат, преобразующий ключ (0) и старое значение в обновлённое значение, которое должно быть сохранено в словаре ("Zero"). Вызывается, если ключ уже существует в словаре.
AddOrUpdate возвращает новое значение для этого ключа (значение, которое было возвращено одним из делегатов).

Чтобы конкурентный словарь работал правильно, может оказаться, что метод AddOrUpdate должен вызвать один (или оба) делегата несколько раз. Такое бывает очень редко, но это возможно. А значит, делегаты должны быть простыми и быстрыми и не должны иметь побочных эффектов, т.е. должны только создавать значение, не изменяя никакие другие переменные в приложении.

Добавить значение в словарь можно и через индекс:
// Добавляет (или обновляет) ключ 0, 
// связывая с ним значение "Zero".
d[0] = "Zero";
Этот синтаксис не предоставляет возможности обновления значений на основании существующего значения. Но он проще и нормально работает, если известно значение, которое требуется сохранить в словаре.

Чтение
bool keyExists = d.TryGetValue(0, out string current);

TryGetValue вернёт true и задаст значение current, если ключ был найден в словаре. Если ключ не найден, TryGetValue вернет false. Прочитать значение можно и через индекс, но при отсутствии ключа будет выдано исключение.

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

Удаление
Аналогично чтению:
bool keyExisted = d.TryRemove(0, out string removed);

Хотя ConcurrentDictionary<TKey, TValue> является потокобезопасным, это не означает атомарности его операций. Если несколько потоков вызывают AddOrUpdate конкурентно, может оказаться, что два потока обнаружат отсутствие ключа, а затем оба одновременно выполнят своего делегата, создающего новое значение.

ConcurrentDictionary<TKey, TValue> хорошо работает при чтении и записи со стороны нескольких потоков в общую коллекцию.
Если обновления относительно редки, возможно, ImmutableDictionary<TKey, TValue> будет более подходящим вариантом.

Источник: Стивен Клири “Конкурентность в C#”. 2-е межд. изд. — СПб.: Питер, 2020. Глава 9.
  • 👍 5
More from @netdeveloperdiary
  1. Oct 14, 2026Post #3373
  2. Oct 13, 2026День 2813. #ЗаметкиНаПолях #Async Ещё Раз Про Async/Await в C#. Часть 1 Почему существует…
  3. Oct 12, 2026День 2812. #Карьера #AI Как Разработчикам Оставаться Полезными, Когда Код Пишет ИИ? По мер…
  4. Oct 11, 2026День 2811. #ВопросыНаСобеседовании Марк Прайс предложил свой набор из 60 вопросов (как тех…
  5. Oct 10, 2026День 2810. #ЧтоНовенького #VSCode Более Быстрый и Лёгкий C# Dev Kit Мы, разработчики, люби…
  6. Oct 9, 2026День 2809. #Карьера 5 Навыков, Которые Помогут Быстрее Стать Сеньором. Окончание Начало 3.…
Threads Profile ViewerView any public Threads profile without an account.Open ThreadLook →Writing with AI? Make it sound human.Metric37 rewrites AI drafts so they read naturally. Free AI detector, 1,500 words free.Try Metric37 →