Що додати для частини оновлення у ConcurrentDictionary AddOrUpdate


109

Я намагаюся переписати якийсь код за допомогою словника для використання 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? ***/);

Будь-які вказівки будуть вдячні.

Відповіді:


220

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

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

Тобто Funcзавжди повертає sessionId, щоб і Add, і Update встановили однакове значення.

BTW: на сторінці MSDN є зразок .


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
Так, атомарний в тому, що це відбувається у всіх відразу і не може наполовину статися або перериватися. Однак не безпечно тим, що хтось інший може змінити його на щось інше безпосередньо перед вашим виконанням, і в цьому випадку зміни втрачені, і ви не знаєте, що це сталося, якщо ви хочете змінити його лише тоді, коли значення є таким, яке ви очікуєте тоді це не робити це для вас.
бродяга

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
Rory

1
Гм так, ви маєте рацію, GetOrAdd () у цьому випадку простіший і достатній - thx для цього підказки!
Ніколя
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.