Вопрос по c#, .net, multithreading, concurrency, thread-safety – Ловушка ConcurrentDictionary - Синхронизированы ли фабрики делегатов из GetOrAdd и AddOrUpdate?

31

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

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

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

Ваш Ответ

2   ответа
34

ConcurrentDictionary, Если вам нужны синхронизированные, это ваша ответственность.

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

Also, although all methods of ConcurrentDictionary are thread-safe, not all methods are atomic, specifically GetOrAdd and AddOrUpdate. The user delegate that is passed to these methods is invoked outside of the dictionary's internal lock. (This is done to prevent unknown code from blocking all threads.)

Смотрите & quot; Как: добавлять и удалять элементы из ConcurrentDictionary

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

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

Поэтому я обертываю вызов метода GetOrAdd в блокировку, но это делает цель ConcurrentDictionary бесполезной. Есть ли лучший способ?
Я немного смущен этим, мы в основном говорим, что если делегат передан в качестве ключа, то его выполнение не будет синхронизировано при оценкеvalue? Или если у нас есть словарь с типом & quot; делегат & quot; в качестве значения ... это когда мы пытаемся выполнить делегат (т.е.dictionary[key](argument);) что исполнение не будет синхронизировано? Или я полностью упускаю суть? Благодарю.
@Snoopy: делегат, оценивающий добавляемое значение, не синхронизирован. Это означает, что если ваш делегат делает что-то, что не является потокобезопаснымand Вы не предприняли попытку синхронизировать эту операцию самостоятельно, тогда произойдут плохие вещи. Но вещь, которую делегат, который действует как фабрика для значения, обычно не делает вещи, которые не являются поточно-ориентированными по своей природе. Большую часть времени делегат, вероятно, просто делаетnew SomeObject() который определенно является потокобезопасным, потому что это операция без сохранения состояния.
@John: часть, которая не объяснена должным образом, но делает ее по-прежнему полезной, заключается в том, что делегаты могут выполняться несколько раз, только если значение ключа не изменилось после того, как делегат сгенерировал новое значение, тогда новое значение будет вставлено, в противном случае - если значение изменилось во время работы делегата, он попытается снова.
23

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

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

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

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

There is no effort to synchronize execution of the delegates. The delegates may get executed more than once since they are invoked inside the loop.

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

Provide your own synchronization for the delegates. Make sure your delegates do not have any side effects that depend on the number of times they are executed.
@SebastianGodelet: я не уверен. Это больше похоже на ошибку, поэтому вполне возможно, что это поведение будет "исправлено" в конце концов.
@BrianGideon: Конечно, извините, я не заметил, что вы говорите о AddOrUpdate, а не GetOrAdd. Спасибо за ответы. Luciano
Изменилось ли это поведение до сих пор?
@Luciano: я только что проверил иGetOrAdd появляется звонитьValueFactory один раз. Так что это толькоAddOrUpdate это может вызвать делегатов несколько раз. Это может быть ошибкой со стороны Microsoft ... не уверен. Я обнаружил это долгое времяanswering a similar question.
Я понимаю, что с точки зрения Ms "не звоните в нашу поддержку". или "будь в безопасности, оставайся дома" который управляет этой реализацией. Это точка зрения риска 0%. Но это так подразумевается под «параллелизмом» прилагательное, что у всех нас теперь есть нерабочий код! По крайней мере, он может быть подписан с предупреждением в документе ... Мне повезло, что я обнаружил, что что-то странное, и выкопал это ... В противном случае нерабочий код был бы выпущен для многих клиентов.

Похожие вопросы