Quoi ajouter pour la partie mise à jour dans ConcurrentDictionary AddOrUpdate

109

J'essaie de réécrire du code en utilisant Dictionary pour utiliser ConcurrentDictionary. J'ai passé en revue quelques exemples mais j'ai toujours du mal à implémenter la fonction AddOrUpdate. Voici le code original:

    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;

Je ne sais pas quoi ajouter pour la partie mise à jour:

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

Tous les pointeurs seraient appréciés.

user438331
la source

Réponses:

220

Vous devez passer un Funcqui renvoie la valeur à stocker dans le dictionnaire en cas de mise à jour. Je suppose que dans votre cas (puisque vous ne faites pas la distinction entre ajouter et mettre à jour), ce serait:

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

C'est-à-dire que le Funcretourne toujours le sessionId, de sorte que Add et Update définissent la même valeur.

BTW: il y a un exemple sur la page MSDN .

M4N
la source
4
Je me battais sérieusement pour simplement trouver une fonction à ajouter ou à mettre à jour à la même valeur. thaks
Zapnologica
2
Bonne réponse. Juste à partir de la signature de AddOrUpdate () affichée dans Visual Studio, vous ne pouvez que deviner la signification des 2 paramètres. Cependant, dans le cas spécifique, que @ user438331 demande, je pense que la solution dans ma réponse en utilisant un simple indexeur est meilleure.
Niklas Peter du
7
Comme le souligne @NiklasPeter ( stackoverflow.com/a/32796165/8479 ), il vaut mieux utiliser simplement l'indexeur normal pour écraser la valeur, car dans votre cas, vous n'êtes pas intéressé par la valeur existante, le cas échéant. Beaucoup plus lisible.
Rory
3
Je recommanderais de changer votre réponse pour diriger les utilisateurs vers la réponse de @NiklasPeter. C'est une bien meilleure solution.
Will Calderwood du
63

J'espère que je n'ai rien manqué dans votre question, mais pourquoi pas comme ça? C'est plus simple, atomique et thread-safe (voir ci-dessous).

userDic[authUser.UserId] = sessionId;

Stocker une paire clé / valeur dans le dictionnaire de manière inconditionnelle, en écrasant toute valeur pour cette clé si la clé existe déjà: utilisez le setter de l'indexeur

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

L'indexeur est également atomique. Si vous passez une fonction à la place, il se peut que ce ne soit pas:

Toutes ces opérations sont atomiques et sécurisées pour les threads en ce qui concerne toutes les autres opérations sur le ConcurrentDictionary. La seule mise en garde concernant l'atomicité de chaque opération concerne celles qui acceptent un délégué, à savoir AddOrUpdate et GetOrAdd. [...] ces délégués sont invoqués en dehors des verrous

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

Niklas Peter
la source
2
Oui atomique en ce sens que cela se produit en tout à la fois et ne peut pas se produire à moitié ou être interrompu. Cependant, ce n'est pas sûr que quelqu'un d'autre puisse le changer en quelque chose d'autre juste avant vous, auquel cas le changement est perdu, et vous ne savez pas que cela s'est produit, si vous ne voulez le changer que si la valeur est ce que vous attendez alors cela ne fera pas cela pour vous.
trampster
26

J'ai fini par implémenter une méthode d'extension:

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);
    }
}
Steve Cook
la source
1

Pour ceux qui sont intéressés, j'implémente actuellement un cas qui est un excellent exemple pour utiliser la "oldValue" aka valeur existante au lieu d'en forcer une nouvelle (personnellement, je n'aime pas le terme "oldValue" car ce n'est pas ça ancien quand il a été créé il y a quelques temps de processeur à partir d'un thread parallèle).

dictionaryCacheQueues.AddOrUpdate(
    uid,
    new ConcurrentQueue<T>(),
    (existingUid, existingValue) => existingValue
);
Nicolas
la source
6
si vous ne souhaitez pas modifier la valeur existante, vous devez utiliser à la GetOrAdd()place msdn.microsoft.com/en-us/library/ee378674(v=vs.110).aspx
Rory
1
Hm oui, vous avez raison, GetOrAdd () est plus simple et suffisant dans ce cas - merci pour cet indice!
Nicolas