Cosa aggiungere per la parte di aggiornamento in ConcurrentDictionary AddOrUpdate


109

Sto cercando di riscrivere del codice usando Dictionary per usare ConcurrentDictionary. Ho esaminato alcuni esempi ma riscontro ancora problemi nell'implementazione della funzione AddOrUpdate. Questo è il codice originale:

    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;

Non so cosa aggiungere per la parte di aggiornamento:

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

Eventuali suggerimenti sarebbero apprezzati.

Risposte:


220

È necessario passare a Funcche restituisce il valore da memorizzare nel dizionario in caso di aggiornamento. Immagino che nel tuo caso (dal momento che non fai distinzione tra aggiungi e aggiorna) questo sarebbe:

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

Cioè Funcrestituisce sempre il sessionId, in modo che sia Add che Update impostino lo stesso valore.

BTW: c'è un esempio nella pagina MSDN .


4
Stavo seriamente lottando per trovare una funzione da aggiungere o aggiornare allo stesso valore. grazie
Zapnologica

2
Buona risposta. Proprio dalla firma dell'AddOrUpdate () visualizzata in Visual Studio si può solo intuire il significato dei 2 parametri. Tuttavia, nel caso specifico, che @ user438331 chiede, penso che la soluzione nella mia risposta utilizzando un semplice indicizzatore sia migliore.
Niklas Peter,

7
Come @NiklasPeter sottolinea ( stackoverflow.com/a/32796165/8479 ), è meglio semplicemente utilizzando l'indicizzatore normale per sovrascrivere il valore, dal momento che nel tuo caso non siete interessati al valore esistente se presente. Molto più leggibile.
Rory

3
Consiglierei di cambiare la tua risposta per indirizzare gli utenti alla risposta di @NiklasPeter. È una soluzione molto migliore.
Will Calderwood,

63

Spero di non essermi perso nulla nella tua domanda, ma perché non proprio così? È più facile, atomico e thread-safe (vedi sotto).

userDic[authUser.UserId] = sessionId;

Memorizza una coppia chiave / valore nel dizionario incondizionatamente, sovrascrivendo qualsiasi valore per quella chiave se la chiave esiste già: usa il setter dell'indicizzatore

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

Anche l'indicizzatore è atomico. Se invece passi una funzione, potrebbe non essere:

Tutte queste operazioni sono atomiche e sono thread-safe rispetto a tutte le altre operazioni su ConcurrentDictionary. L'unico avvertimento sull'atomicità di ciascuna operazione è per quelle che accettano un delegato, ovvero AddOrUpdate e GetOrAdd. [...] questi delegati vengono richiamati al di fuori dei blocchi

Vedi: http://blogs.msdn.com/b/pfxteam/archive/2010/01/08/9945809.aspx


2
Sì atomico in quanto accade tutto in una volta e non può accadere a metà o essere interrotto. Tuttavia non è sicuro in quanto qualcun altro può cambiarlo in qualcos'altro appena prima di farlo, nel qual caso il cambiamento è perso e non sai che è successo, se vuoi cambiarlo solo se il valore è quello che ti aspetti allora questo non lo farà per te.
Trampster

26

Ho finito per implementare un metodo di estensione:

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

Per coloro che sono interessati, sto attualmente implementando un caso che è un ottimo esempio per utilizzare "oldValue" aka valore esistente invece di forzarne uno nuovo (personalmente non mi piace il termine "oldValue" perché non è quello vecchio quando è stato creato solo pochi tick del processore fa da un thread parallelo).

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

6
se non vuoi cambiare il valore esistente dovresti usare GetOrAdd()invece msdn.microsoft.com/en-us/library/ee378674(v=vs.110).aspx
Rory

1
Hm sì, hai ragione, GetOrAdd () è più semplice e abbastanza in questo caso - grazie per questo suggerimento!
Nicolas
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.