Подтвердить что ты не робот

ConcurrentDictionary Pitfall - Являются ли делегированные заводы из GetOrAdd и AddOrUpdate синхронизированными?

Документация ConcurrentDictionary не содержит явного состояния, поэтому я думаю, мы не можем ожидать, что делегаты valueFactory и updateValueFactory синхронизируются с их выполнением (из операций GetOrAdd() и AddOrUpdate() соответственно).

Итак, я думаю, что мы не можем реализовать использование ресурсов внутри них, которые нуждаются в параллельном управлении без ручного внедрения нашего собственного параллельного элемента управления, возможно, просто используя [MethodImpl(MethodImplOptions.Synchronized)] над делегатами.

Я прав? Или тот факт, что ConcurrentDictionary является потокобезопасным, мы можем ожидать, что вызовы этих делегатов будут автоматически синхронизированы (поточно-безопасные тоже)?

4b9b3361

Ответ 1

Да, вы правы, делегаты пользователей не синхронизируются с помощью ConcurrentDictionary. Если вам нужны синхронизированные, это ваша ответственность.

Сам MSDN говорит:

Кроме того, хотя все методы ConcurrentDictionary являются поточно-безопасный, не все методы являются атомарными, в частности GetOrAdd и AddOrUpdate. Делегат пользователя, который передается этим методам, вызывается вне внутренней блокировки словаря. (Это делается для запретить неизвестный код блокировать все потоки.)

См." Как добавить и удалить элементы из ConcurrentDictionary

Это связано с тем, что ConcurrentDictionary не имеет представления о том, что сделает предоставленный вами делегат, или о его производительности, поэтому, если он попытается заблокировать их, это может негативно повлиять на производительность и разрушить значение ConcurrentDictionary.

Таким образом, ответственность за синхронизацию их делегата несет пользователь, если это необходимо. Ссылка MSDN выше на самом деле имеет хороший пример гарантий, которые она делает и не делает.

Ответ 2

Эти делегаты не только не синхронизированы, но даже не гарантируются, что они будут выполняться только один раз. Фактически они могут выполняться несколько раз за вызов до AddOrUpdate.

Например, алгоритм для AddOrUpdate выглядит примерно так.

TValue value;
do
{
  if (!TryGetValue(...))
  {
    value = addValueFactory(key);
    if (!TryAddInternal(...))
    {
      continue;
    }
    return value;
  }
  value = updateValueFactory(key);
} 
while (!TryUpdate(...))
return value;

Обратите внимание на две вещи.

  • Нет необходимости синхронизировать выполнение делегатов.
  • Делегаты могут выполняться несколько раз, так как они вызываются внутри цикла.

Итак, вам нужно убедиться, что вы делаете две вещи.

  • Предоставьте свою собственную синхронизацию для делегатов.
  • Убедитесь, что ваши делегаты не имеют никаких побочных эффектов, которые зависят от количества выполняемых им времени.