Почему при добавлении в ConcurrentDictionary не всегда гарантируется успешное добавление и нужна логика ретрая?
Что проверяют
Понимаете ли вы, что ConcurrentDictionary даёт атомарность отдельной операции, но не атомарность вашего сценария «прочитал, посчитал, записал». Формулировка с подвохом: сильный кандидат разберёт, когда ретрай действительно нужен, а когда он бесконечный.
Как отвечать
Разделить два случая.
TryAdd возвращает false ровно в одной ситуации: ключ уже есть. Это не сбой и не временная неудача, повторять её бессмысленно, через сто миллисекунд ключ никуда не денется. Цикл ретраев вокруг TryAdd это признак непонимания контракта.
Ретрай нужен там, где словарь работает по оптимистичной схеме. TryUpdate(key, newValue, comparisonValue) меняет значение, только если текущее совпало с ожидаемым. Если между вашим чтением и записью другой поток успел положить своё, TryUpdate вернёт false, и цикл надо начинать заново с нового чтения:
while (true)
{
if (!dict.TryGetValue(key, out var current))
{
if (dict.TryAdd(key, Init())) break;
continue;
}
if (dict.TryUpdate(key, Next(current), current)) break;
}Отдельно про фабрики: GetOrAdd и AddOrUpdate вызывают делегат вне блокировки, поэтому при гонке он может выполниться несколько раз, а лишний результат будет выброшен. Вернётся всегда то значение, которое реально лежит в словаре, но побочные эффекты внутри фабрики продублируются.
Где спотыкаются
Пишут цикл с Thread.Sleep вокруг TryAdd и называют это обработкой конфликта. Занятый ключ не освободится сам, так что код либо честно упрётся в лимит попыток, либо задержит поток на ровном месте.
Считают, что потокобезопасный словарь делает потокобезопасным весь код. Атомарна каждая операция по отдельности, а пара ContainsKey плюс присваивание уже нет. Составное действие собирается из GetOrAdd, AddOrUpdate и цикла на TryUpdate, иначе нужен внешний замок.
Забывают цену обзорных операций. Count и Clear захватывают все внутренние замки, перечисление идёт по живому словарю и не даёт согласованного среза, а снимок берёт ToArray. На горячем словаре счётчик в логе способен стоить дороже самой работы.