Синхронизация потоков в .NET
Вопрос
Как вы обеспечиваете потокобезопасность в C#-коде?
Попробуйте сами
Ответьте вслух за 2 минуты, как на собесе. Потом сверьтесь с тезисами: что прозвучало, а что нет.
Показать ответ и разбор
Коротко
Что должно прозвучать в ответе
Сначала избегать общего изменяемого состояния: неизменяемые объекты, локальные данные
lock для коротких критических секций, на приватном объекте
Interlocked для счётчиков и флагов
Конкурентные коллекции: ConcurrentDictionary, ConcurrentQueue, Channel
SemaphoreSlim для асинхронного кода и ограничения параллелизма
Порядок захвата блокировок против дедлоков
Что могут спросить следом
- Почему нельзя await внутри lock?
- Чем ConcurrentDictionary лучше Dictionary под lock?
- Как найти дедлок в проде?
Разбор
Лучшая синхронизация та, которая не нужна. Поэтому сначала стараюсь не разделять изменяемое состояние: неизменяемые объекты и record, данные в локальных переменных, передача сообщений через Channel<T>.
Если общее состояние есть, выбираю инструмент по задаче. Для счётчиков и флагов Interlocked, он без блокировок. Для коротких критических секций lock на приватном readonly-объекте, без вызовов внешнего кода и без await внутри. Для словарей и очередей конкурентные коллекции: ConcurrentDictionary с GetOrAdd и AddOrUpdate.
В асинхронном коде использую SemaphoreSlim с WaitAsync и Release в finally, он же помогает ограничить параллелизм. Чтобы не ловить дедлоки, захватываю несколько блокировок всегда в одном порядке и держу их как можно меньше. Для диагностики в проде снимаю дамп dotnet-dump и смотрю стеки потоков через clrstack и syncblk.
Ответьте сами, а не прочитайте
В тренажёре вопрос попадёт в повторения и вернётся, когда начнёт забываться. Плюс билеты с зачётом: 20 вопросов, не больше 2 ошибок.