ConcurrentDictionary AddOrUpdate

Я пытаюсь переписать код с помощью словаря для использования ConcurrentDictionary. Я рассмотрел некоторые примеры, но мне все еще не удается реализовать функцию AddOrUpdate. Это исходный код:

    dynamic a = HttpContext;
    Dictionary<int, string> userDic = this.HttpContext.Application["UserSessionList"] as Dictionary<int, String>;

   if (userDic != null)
   {
      if (useDic.ContainsKey(authUser.UserId))
      {
        userDic.Remove(authUser.UserId);
      }
   }
  else
  {
     userDic = new Dictionary<int,string>();
  }
  userDic.Add(authUser.UserId, a.Session.SessionID.ToString());
  this.HttpContext.Application["UserDic"] = userDic;

Я не знаю, что добавить для части обновления:

userDic.AddOrUpdate(authUser.UserId,
                    a.Session.SessionID.ToString(),
                    /*** what to add here? ***/);

Любые указатели будут оценены.

Ответ 1

Вам нужно передать Func, который возвращает значение, которое будет храниться в словаре в случае обновления. Я думаю, в вашем случае (поскольку вы не различаете добавление и обновление) это будет:

var sessionId = a.Session.SessionID.ToString();
userDic.AddOrUpdate(
  authUser.UserId,
  sessionId,
  (key, oldValue) => sessionId);

т.е. Func всегда возвращает sessionId, так что и Add, и Update устанавливают одно и то же значение.

BTW: есть образец на странице MSDN.

Ответ 2

Я надеюсь, что я ничего не пропустил в вашем вопросе, но почему бы не просто так? Это проще, атомно и поточно-безопасно (см. Ниже).

userDic[authUser.UserId] = sessionId;

Храните пару ключ/значение в словаре безоговорочно, переписывая любое значение для этого ключа, если ключ уже существует: используйте установщик индексаторов

(см. http://blogs.msdn.com/b/pfxteam/archive/2010/01/08/9945809.aspx)

Индексатор также является атомарным. Если вы передадите функцию вместо этого, это может быть не так:

Все эти операции являются атомарными и являются потокобезопасными применительно ко всем другим операциям в ConcurrentDictionary. Единственное предостережение от атомарности каждой операции - для тех, кто принимает делегата, а именно AddOrUpdate и GetOrAdd. [...] эти делегаты выходят за пределы блокировок

Смотрите: http://blogs.msdn.com/b/pfxteam/archive/2010/01/08/9945809.aspx

Ответ 3

В результате я использовал метод расширения:

static class ExtensionMethods
{
    // Either Add or overwrite
    public static void AddOrUpdate<K, V>(this ConcurrentDictionary<K, V> dictionary, K key, V value)
    {
        dictionary.AddOrUpdate(key, value, (oldkey, oldvalue) => value);
    }
}

Ответ 4

Для тех, кто заинтересован в этом, я в настоящее время реализую случай, который является отличным примером использования "oldValue" как существующего значения вместо того, чтобы форсировать новый (лично мне не нравится термин "oldValue", поскольку он не так уж и старый, когда он был создан всего лишь несколько процессоров тиков назад изнутри параллельного потока).

dictionaryCacheQueues.AddOrUpdate(
    uid,
    new ConcurrentQueue<T>(),
    (existingUid, existingValue) => existingValue
);