Was für den Update-Teil in ConcurrentDictionary AddOrUpdate hinzuzufügen ist

109

Ich versuche, Code mit Dictionary neu zu schreiben, um ConcurrentDictionary zu verwenden. Ich habe einige Beispiele überprüft, habe jedoch immer noch Probleme bei der Implementierung der AddOrUpdate-Funktion. Dies ist der Originalcode:

    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;

Ich weiß nicht, was ich für den Update-Teil hinzufügen soll:

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

Alle Hinweise wäre dankbar.

user438331
quelle

Antworten:

220

Sie müssen a übergeben, Funcdas den Wert zurückgibt, der im Falle einer Aktualisierung im Wörterbuch gespeichert werden soll. Ich denke in Ihrem Fall (da Sie nicht zwischen Hinzufügen und Aktualisieren unterscheiden) wäre dies:

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

FuncDh das gibt immer die sessionId zurück, so dass sowohl Add als auch Update den gleichen Wert setzen.

Übrigens: Auf der MSDN-Seite befindet sich ein Beispiel .

M4N
quelle
4
Ich habe ernsthaft darum gekämpft, nur eine Funktion zu finden, um denselben Wert hinzuzufügen oder zu aktualisieren. thaks
Zapnologica
2
Gute Antwort. Nur anhand der in Visual Studio angezeigten Signatur von AddOrUpdate () können Sie nur die Bedeutung der beiden Parameter erraten. In dem speziellen Fall, nach dem @ user438331 fragt, denke ich jedoch, dass die Lösung in meiner Antwort mit einem einfachen Indexer besser ist.
Niklas Peter
7
Wie @NiklasPeter hervorhebt ( stackoverflow.com/a/32796165/8479 ), ist es besser, den normalen Indexer zum Überschreiben des Werts zu verwenden, da Sie in Ihrem Fall nicht an dem vorhandenen Wert interessiert sind, falls vorhanden. Viel besser lesbar.
Rory
3
Ich würde empfehlen, Ihre Antwort so zu ändern, dass Benutzer auf die Antwort von @NiklasPeter verweisen. Es ist eine viel bessere Lösung.
Will Calderwood
63

Ich hoffe, dass ich in Ihrer Frage nichts verpasst habe, aber warum nicht einfach so? Es ist einfacher, atomar und threadsicher (siehe unten).

userDic[authUser.UserId] = sessionId;

Speichern Sie ein Schlüssel / Wert-Paar bedingungslos im Wörterbuch und überschreiben Sie einen beliebigen Wert für diesen Schlüssel, wenn der Schlüssel bereits vorhanden ist: Verwenden Sie den Setter des Indexers

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

Der Indexer ist auch atomar. Wenn Sie stattdessen eine Funktion übergeben, ist dies möglicherweise nicht:

Alle diese Operationen sind atomar und in Bezug auf alle anderen Operationen im ConcurrentDictionary threadsicher. Die einzige Einschränkung für die Atomizität jeder Operation betrifft diejenigen, die einen Delegaten akzeptieren, nämlich AddOrUpdate und GetOrAdd. [...] Diese Delegaten werden außerhalb der Sperren aufgerufen

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

Niklas Peter
quelle
2
Ja, atomar, da es auf einmal passiert und nicht zur Hälfte passieren oder unterbrochen werden kann. Es ist jedoch nicht sicher, dass jemand anderes es kurz vor Ihrer Ausführung in etwas anderes ändern kann. In diesem Fall geht die Änderung verloren, und Sie wissen nicht, dass es passiert ist, wenn Sie es nur ändern möchten, wenn der Wert dem entspricht, den Sie dann erwarten das wird das nicht für dich tun.
Trampster
26

Am Ende habe ich eine Erweiterungsmethode implementiert:

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
quelle
1

Für diejenigen, die daran interessiert sind, implementiere ich derzeit einen Fall, der ein großartiges Beispiel für die Verwendung des "alten Werts" oder des vorhandenen Werts ist, anstatt einen neuen zu erzwingen (persönlich mag ich den Begriff "alter Wert" nicht, da dies nicht der Fall ist alt, als es erst vor wenigen Prozessor-Ticks innerhalb eines parallelen Threads erstellt wurde).

dictionaryCacheQueues.AddOrUpdate(
    uid,
    new ConcurrentQueue<T>(),
    (existingUid, existingValue) => existingValue
);
Nicolas
quelle
6
Wenn Sie den vorhandenen Wert nicht ändern möchten, sollten Sie GetOrAdd()stattdessen msdn.microsoft.com/en-us/library/ee378674(v=vs.110).aspx
Rory
1
Hm ja, du hast recht, GetOrAdd () ist in diesem Fall einfacher und genug - danke für diesen Hinweis!
Nicolas