Ich konnte nicht genügend Informationen zu ConcurrentDictionary
Typen finden und dachte, ich würde hier danach fragen.
Derzeit verwende ich a Dictionary
, um alle Benutzer zu speichern, auf die ständig von mehreren Threads zugegriffen wird (aus einem Thread-Pool, also keine genaue Anzahl von Threads), und der Zugriff ist synchronisiert.
Ich habe kürzlich herausgefunden, dass es in .NET 4.0 eine Reihe von thread-sicheren Sammlungen gibt, und es scheint sehr erfreulich zu sein. Ich habe mich gefragt, was die Option "effizienter und einfacher zu verwalten" wäre, da ich die Option habe, einen normalen Dictionary
mit synchronisiertem Zugriff zu haben oder einen, ConcurrentDictionary
der bereits threadsicher ist.
Antworten:
Eine thread-sichere Sammlung im Vergleich zu einer nicht thread-sicheren Sammlung kann auf andere Weise betrachtet werden.
Stellen Sie sich ein Geschäft ohne Angestellten vor, außer an der Kasse. Sie haben eine Menge Probleme, wenn die Leute nicht verantwortungsbewusst handeln. Nehmen wir zum Beispiel an, ein Kunde nimmt eine Dose aus einer Pyramidendose, während ein Angestellter gerade die Pyramide baut. Die Hölle bricht los. Oder was ist, wenn zwei Kunden gleichzeitig nach demselben Artikel greifen und wer gewinnt? Wird es einen Kampf geben? Dies ist eine nicht threadsichere Sammlung. Es gibt viele Möglichkeiten, um Probleme zu vermeiden, aber alle erfordern eine Art Sperre oder eher einen expliziten Zugriff auf die eine oder andere Weise.
Stellen Sie sich andererseits ein Geschäft mit einem Angestellten an einem Schreibtisch vor, und Sie können nur über ihn einkaufen. Du stellst dich an und fragst ihn nach einem Gegenstand, er bringt ihn dir zurück und du verlässt die Schlange. Wenn Sie mehrere Artikel benötigen, können Sie auf jeder Rundreise nur so viele Artikel abholen, wie Sie sich erinnern können. Sie müssen jedoch vorsichtig sein, um den Angestellten nicht zu belästigen. Dies wird die anderen Kunden in der Schlange hinter Ihnen verärgern.
Betrachten Sie dies nun. Was ist, wenn Sie im Laden mit einem Angestellten ganz vorne mit dabei sind und den Angestellten fragen: "Haben Sie Toilettenpapier?", Und er sagt "Ja", und dann sagen Sie "Ok, ich". Ich melde mich bei Ihnen, wenn ich weiß, wie viel ich brauche. Wenn Sie dann wieder ganz vorne mit dabei sind, kann der Laden natürlich ausverkauft sein. Dieses Szenario wird durch eine threadsichere Sammlung nicht verhindert.
Eine threadsichere Sammlung garantiert, dass ihre internen Datenstrukturen jederzeit gültig sind, auch wenn von mehreren Threads aus darauf zugegriffen wird.
Für eine nicht threadsichere Sammlung gibt es keine solchen Garantien. Wenn Sie beispielsweise einem Binärbaum in einem Thread etwas hinzufügen, während ein anderer Thread damit beschäftigt ist, den Baum neu auszugleichen, gibt es keine Garantie dafür, dass das Element hinzugefügt wird, oder auch wenn der Baum danach noch gültig ist, kann er hoffnungslos beschädigt sein.
Eine threadsichere Sammlung garantiert jedoch nicht, dass sequentielle Operationen am Thread alle mit demselben "Snapshot" seiner internen Datenstruktur arbeiten. Wenn Sie also Code wie diesen haben:
Möglicherweise erhalten Sie eine NullReferenceException, da zwischen
tree.Count
und eintree.First()
anderer Thread die verbleibenden Knoten im Baum gelöscht hat, was bedeutet, dass zurückgegebenFirst()
wirdnull
.In diesem Szenario müssen Sie entweder prüfen, ob die betreffende Sammlung einen sicheren Weg bietet, um das zu erhalten, was Sie möchten. Möglicherweise müssen Sie den obigen Code neu schreiben oder Sie müssen möglicherweise sperren.
quelle
Dictionary
und der Handhabung der Sperre selbst und der Verwendung desConcurrentDictionary
in .NET 4+ integrierten Typs. Ich bin eigentlich ein bisschen verblüfft, dass dies akzeptiert wurde.Sie müssen immer noch sehr vorsichtig sein, wenn Sie threadsichere Sammlungen verwenden, da threadsicher nicht bedeutet, dass Sie alle Threading-Probleme ignorieren können. Wenn sich eine Sammlung als threadsicher bewirbt, bedeutet dies normalerweise, dass sie auch dann in einem konsistenten Zustand bleibt, wenn mehrere Threads gleichzeitig lesen und schreiben. Dies bedeutet jedoch nicht, dass ein einzelner Thread eine "logische" Folge von Ergebnissen sieht, wenn er mehrere Methoden aufruft.
Wenn Sie beispielsweise zuerst prüfen, ob ein Schlüssel vorhanden ist, und später den Wert abrufen, der dem Schlüssel entspricht, ist dieser Schlüssel möglicherweise auch bei einer ConcurrentDictionary-Version nicht mehr vorhanden (da ein anderer Thread den Schlüssel möglicherweise entfernt hat). In diesem Fall müssen Sie weiterhin die Sperrung verwenden (oder besser: Kombinieren Sie die beiden Aufrufe mithilfe von TryGetValue ).
Verwenden Sie sie also, aber denken Sie nicht, dass Sie damit kostenlos alle Parallelitätsprobleme ignorieren können. Sie müssen immer noch vorsichtig sein.
quelle
TryGetValue
immer ein Teil desDictionary
empfohlenen Ansatzes. Die wichtigen "gleichzeitigen" Methoden, dieConcurrentDictionary
eingeführt werden , sindAddOrUpdate
undGetOrAdd
. Also, gute Antwort, hätte aber ein besseres Beispiel wählen können.Intern verwendet ConcurrentDictionary für jeden Hash-Bucket eine separate Sperre. Solange Sie nur Add / TryGetValue und ähnliche Methoden verwenden, die für einzelne Einträge funktionieren, funktioniert das Wörterbuch als nahezu sperrenfreie Datenstruktur mit dem jeweiligen süßen Leistungsvorteil. OTOH Die Aufzählungsmethoden (einschließlich der Count-Eigenschaft) sperren alle Buckets auf einmal und sind daher in Bezug auf die Leistung schlechter als ein synchronisiertes Wörterbuch.
Ich würde sagen, verwenden Sie einfach ConcurrentDictionary.
quelle
Ich denke, dass die ConcurrentDictionary.GetOrAdd-Methode genau das ist, was die meisten Multithread-Szenarien benötigen.
quelle
Haben Sie die Reactive Extensions für .Net 3.5sp1 gesehen ? Laut Jon Skeet haben sie ein Bündel der parallelen Erweiterungen und gleichzeitigen Datenstrukturen für .Net3.5 sp1 zurückportiert.
Für .Net 4 Beta 2 gibt es eine Reihe von Beispielen, in denen die Verwendung der parallelen Erweiterungen ausführlich beschrieben wird.
Ich habe gerade die letzte Woche damit verbracht, das ConcurrentDictionary mit 32 Threads zu testen, um E / A durchzuführen. Es scheint wie angekündigt zu funktionieren, was darauf hindeutet, dass eine enorme Menge an Tests durchgeführt wurde.
Bearbeiten : .NET 4 ConcurrentDictionary und Muster.
Microsoft hat ein PDF mit dem Namen Patterns of Paralell Programming veröffentlicht. Es lohnt sich wirklich, es herunterzuladen, da es die richtigen Muster für .Net 4 Concurrent-Erweiterungen und die zu vermeidenden Anti-Muster ausführlich beschreibt. Hier ist es.
quelle
Grundsätzlich möchten Sie mit dem neuen ConcurrentDictionary gehen. Sie müssen sofort weniger Code schreiben, um threadsichere Programme zu erstellen.
quelle
Dies
ConcurrentDictionary
ist eine großartige Option, wenn alle Ihre Sicherheitsanforderungen erfüllt sind . Wenn dies nicht der Fall ist , mit anderen Worten, wenn Sie etwas etwas Komplexes tun, ist ein normalesDictionary
+lock
möglicherweise die bessere Option. Angenommen, Sie fügen einem Wörterbuch einige Bestellungen hinzu und möchten den Gesamtbetrag der Bestellungen auf dem neuesten Stand halten. Sie können Code wie folgt schreiben:Dieser Code ist nicht threadsicher. Mehrere Threads, die das
_totalAmount
Feld aktualisieren , können es in einem beschädigten Zustand belassen. Sie können also versuchen, es mit einemlock
:Dieser Code ist "sicherer", aber immer noch nicht threadsicher. Es gibt keine Garantie dafür, dass das
_totalAmount
mit den Einträgen im Wörterbuch übereinstimmt. Ein anderer Thread versucht möglicherweise, diese Werte zu lesen, um ein UI-Element zu aktualisieren:Die
totalAmount
Anzahl entspricht möglicherweise nicht der Anzahl der Bestellungen im Wörterbuch. Die angezeigten Statistiken könnten falsch sein. An diesem Punkt werden Sie feststellen, dass Sie denlock
Schutz um die Aktualisierung des Wörterbuchs erweitern müssen:Dieser Code ist absolut sicher, aber alle Vorteile der Verwendung von a
ConcurrentDictionary
sind weg.Dictionary
, da die interne Verriegelung im InnerenConcurrentDictionary
jetzt verschwenderisch und überflüssig ist.TryAdd
?,AddOrUpdate
?).Mein Rat lautet also: Beginnen Sie mit einem
Dictionary
+lock
und behalten Sie die Option, später ein Upgrade auf aConcurrentDictionary
als Leistungsoptimierung durchzuführen, wenn diese Option tatsächlich praktikabel ist. In vielen Fällen wird es nicht sein.quelle
Wir haben ConcurrentDictionary für die zwischengespeicherte Sammlung verwendet, die alle 1 Stunde neu gefüllt und dann von mehreren Client-Threads gelesen wird, ähnlich der Lösung für Ist dieser Beispiel-Thread sicher? Frage.
Wir haben festgestellt, dass die Änderung in ReadOnlyDictionary die Gesamtleistung verbessert.
quelle