Что добавить для части обновления в ConcurrentDictionary AddOrUpdate

109

Я пытаюсь переписать код с помощью Dictionary, чтобы использовать 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? ***/);

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

user438331
источник

Ответы:

220

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

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

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

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

M4N
источник
4
Я серьезно боролся, чтобы просто найти функцию для добавления или обновления того же значения. thaks
Zapnologica
2
Хороший ответ. Просто по сигнатуре AddOrUpdate (), отображаемой в Visual Studio, вы можете только догадываться о значении двух параметров. Однако в конкретном случае, о котором спрашивает @ user438331, я думаю, что решение в моем ответе с использованием простого индексатора лучше.
Никлас Питер
7
Как указывает @NiklasPeter ( stackoverflow.com/a/32796165/8479 ), вам лучше просто использовать обычный индексатор для перезаписи значения, поскольку в вашем случае вас не интересует существующее значение, если оно есть. Намного более читабельным.
Рори
3
Я бы рекомендовал изменить ваш ответ, чтобы указывать пользователям на ответ @NiklasPeter. Это гораздо лучшее решение.
Уилл Колдервуд
63

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

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

Никлас Питер
источник
2
Да, атомарно в том смысле, что это происходит сразу и не может произойти наполовину или прерваться. Однако небезопасно в том смысле, что кто-то другой может изменить его на что-то еще прямо перед вами, и в этом случае изменение потеряно, и вы не знаете, что это произошло, если вы хотите изменить его только в том случае, если значение такое, как вы ожидаете, тогда это не сделает этого для вас.
trampster 07
26

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

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);
    }
}
Стив Кук
источник
1

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

dictionaryCacheQueues.AddOrUpdate(
    uid,
    new ConcurrentQueue<T>(),
    (existingUid, existingValue) => existingValue
);
никола
источник
6
если вы не хотите изменять существующее значение, вы должны использовать GetOrAdd()вместо этого msdn.microsoft.com/en-us/library/ee378674(v=vs.110).aspx
Рори
1
Хм, да, вы правы, GetOrAdd () проще и в данном случае достаточно - спасибо за подсказку!
Николас