Frage

Ich konnte nicht genug Informationen über ConcurrentDictionary Typen finden, so dass ich dachte, dass ich danach fragen würde hier.

Zur Zeit verwende ich eine Dictionary alle Benutzer zu halten, die ständig von mehreren Threads zugegriffen wird (von einem Thread-Pool, so dass keine genaue Menge von Threads), und es hat den Zugriff synchronisiert.

Vor kurzem fand ich heraus, dass es eine Reihe von Thread-sicheren Sammlungen in .NET 4.0 war, und es scheint zu gefallen sehr werden. Ich frage mich, was wäre die ‚effizient und einfacher zu verwalten‘ Option, wie ich die Möglichkeit habe, ein normales Dictionary mit synchronisierten Zugriff zwischen Atomen oder ein ConcurrentDictionary hat, die bereits Thread-sicher.

Verweis auf .NET 4.0 ist ConcurrentDictionary

War es hilfreich?

Lösung

Eine Thread-Sammlung gegen ein nicht-THREAD-Sammlung kann auf eine andere Weise auf betrachtet werden.

ein Geschäft ohne Schreiber Man denke an der Kasse ausnehmen. Sie haben eine Menge Probleme, wenn die Menschen verantwortungsvoll handeln nicht. Zum Beispiel, sagen sie mal ein Kunde eine Dose aus einer pyramide Dose nimmt, während ein Angestellter derzeit die Pyramide bauen, die Hölle würde losbrechen. Oder was passiert, wenn zwei Kunden für den gleichen Artikel in der gleichen Zeit erreicht, wer gewinnt? Wird es ein Kampf sein? Dies ist eine nicht-THREAD-Sammlung. Es gibt viele Möglichkeiten, um Probleme zu vermeiden, aber sie alle erfordern eine Art von Verriegelung, oder eher expliziten Zugriff auf eine oder andere Weise.

Auf der anderen Seite, sollen Sie ein Geschäft mit einem Schreiber an einem Schreibtisch, und man kann durch ihn nur einkaufen. Sie in der Schlange bekommen, und ihn für einen Artikel stellen, bringt er es zurück zu Ihnen, und Sie gehen aus der Leitung. Wenn Sie mehrere Artikel benötigen, können Sie nur so viele Punkte auf jeder Hin- und Rück holen wie Sie sich erinnern können, aber Sie müssen vorsichtig sein, um zu vermeiden, den Schreiber hogging, wird dies die anderen Kunden in der Schlange hinter euch erzürnen.

Nun ist diese berücksichtigen. Im Geschäft mit einem Schreiber, was passiert, wenn man den ganzen Weg an die Spitze der Schlange bekommen, und fragen Sie den Schreiber „Haben Sie Toilettenpapier haben“, und er sagt: „Ja“, und dann gehen Sie „Ok, I‘ ll auf Sie zurückkommen, wenn ich, wie viel ich brauche“kennen, dann durch die Zeit, die Sie an der Vorderseite der Linie sind zurück, kann das Geschäft natürlich ausverkauft sein. Dieses Szenario wird durch eine Thread-Sammlung nicht verhindert wird.

Eine THREAD Sammlung garantiert, dass seine internen Datenstrukturen zu allen Zeiten gültig sind, auch wenn sie von mehreren Threads zugegriffen.

Eine nicht-Thread-Kollektion kommt nicht mit solchen Garantien. Zum Beispiel, wenn Sie etwas zu einem binären Baum auf einem Thread, während ein anderer Thread beschäftigt Rebalancing ist der Baum hinzufügen, gibt es keine Garantie, den Punkt hinzugefügt werden, oder auch, dass der Baum später noch gültig ist, könnte es beschädigt jenseits Hoffnung sein.

Eine Thread-Sammlung nicht jedoch gewährleistet, dass aufeinanderfolgende Vorgänge auf dem Thread arbeiten alle auf dem gleichen „Schnappschuss“ der internen Datenstruktur, das heißt, wenn Sie Code wie folgt aussehen:

if (tree.Count > 0)
    Debug.WriteLine(tree.First().ToString());

Sie können eine Nullreferenceexception, weil dazwischen tree.Count und tree.First(), hat ein anderer Thread die verbleibenden Knoten im Baum ausgeräumt, die Mittel First() null zurückkehren wird.

Für dieses Szenario müssen Sie entweder sehen, ob die Sammlung in Frage eine sichere Art und Weise hat zu bekommen, was Sie wollen, vielleicht müssen Sie den Code oben neu zu schreiben, oder Sie vielleicht Sperre benötigen.

Andere Tipps

Sie müssen noch sehr vorsichtig sein, wenn Thread-sichere Sammlungen verwenden, da Thread-sicher nicht bedeutet, Sie alle Thread-Probleme ignorieren können. Wenn eine Sammlung selbst als threadsicher bewirbt, ist es in der Regel bedeutet, dass es sogar in einem konsistenten Zustand bleibt, wenn mehrere Threads gleichzeitig das Lesen und Schreiben. Aber das bedeutet nicht, dass ein einzelner Thread eine „logische“ Folge der Ergebnisse sehen, wenn es mehrere Methoden aufruft.

Zum Beispiel, wenn Sie zunächst prüfen, ob ein Schlüssel vorhanden ist und erhalten dann später den Wert, entspricht den Schlüssel, kann dieser Schlüssel nicht mehr existiert sogar mit einer ConcurrentDictionary Version (weil ein anderer Thread den Schlüssel entfernt haben könnte). Sie müssen noch auf den Einsatz Verriegelung in diesem Fall (oder besser: Kombinieren Sie die beiden Anrufe mithilfe von TryGetValue ).

sie so verwenden, aber glaube nicht, dass es Ihnen eine Freikarte gibt alle Concurrency Probleme zu ignorieren. Sie müssen noch vorsichtig sein.

Intern ConcurrentDictionary verwendet eine separate Sperre für jeden Hash-Behälter. Solange Sie nur Add / TryGetValue und ähnliche Methoden, die Arbeit an einzelnen Einträgen, funktioniert das Wörterbuch als fast schleusenfreien Datenstruktur mit dem jeweiligen süßen Leistungsvorteil. OTOH die Zählverfahren (einschließlich der Count-Eigenschaft) sperren Sie alle Eimer auf einmal und sind somit schlechter als eine synchronisierte Wörterbuch, Performance-weise.

Ich würde sagen, nur ConcurrentDictionary verwenden.

Ich denke, dass ConcurrentDictionary.GetOrAdd Methode ist genau das, was die meisten Multi-Threaded-Szenarien benötigen.

Haben Sie das gesehen Reactive Extensions für .Net 3.5sp1. Laut Jon Skeet, haben sie ein Bündel der parallelen Erweiterungen und gleichzeitige Datenstrukturen für NET3.5 sp1 zurückzuportiert.

Es gibt eine Reihe von Proben für .Net 4 Beta 2, die auf in ziemlich gut beschreibt detailliert, wie sie Erweiterungen der parallel zu verwenden.

Ich habe gerade die letzte Woche verbrachte die ConcurrentDictionary Prüfung 32 Threads eine E / O. Es scheint zu funktionieren wie in der Werbung, die eine enorme Menge an Testhineingesteckt wurde, hinweisen.

Bearbeiten :. .NET 4 ConcurrentDictionary und Muster

Microsoft haben ein PDF namens Patterns of Paralell Programmierung freigegeben. Sein reallly wert, heruntergeladen, wie es beschrieben in wirklich netten Details, die richtigen Muster zu verwenden für .Net 4 Concurrent-Erweiterungen und das anti-Muster zu vermeiden. Hier ist es.

Im Grunde wollen Sie mit dem neuen ConcurrentDictionary gehen. Direkt aus der Box müssen Sie weniger Code zu machen Threadsicherheit Programme schreiben.

Wir haben ConcurrentDictionary für Cache-Sammlung verwendet, dass jeder 1 Stunde wieder aufgefüllt ist und dann von mehreren Client-Threads gelesen, ähnlich zu Ist die Lösung für die dieses Beispiel Thread-sicher? Frage.

Wir fanden, dass es Wechsel auf ReadOnlyDictionary Gesamtleistung verbessert .

Die ConcurrentDictionary ist eine gute Option, wenn es erfüllt alle Ihre Thread-Sicherheit braucht. Wenn es nicht, mit anderen Worten von Ihnen etwas etwas komplexer machen, kann ein normal Dictionary + lock eine bessere Option sein. Zum Beispiel können sagen, dass Sie einige Aufträge in einem Wörterbuch hinzufügen, und Sie wollen immer die Gesamtmenge der Aufträge aktualisiert. Sie können Code wie folgt schreiben:

private ConcurrentDictionary<int, Order> _dict;
private Decimal _totalAmount = 0;

while (await enumerator.MoveNextAsync())
{
    Order order = enumerator.Current;
    _dict.TryAdd(order.Id, order);
    _totalAmount += order.Amount;
}

Dieser Code ist nicht Thread-sicher. Mehrere Threads die _totalAmount Feld Aktualisierung kann es in einem beschädigten Zustand verlassen. So können Sie versuchen, es mit einem lock zu schützen:

_dict.TryAdd(order.Id, order);
lock (_locker) _totalAmount += order.Amount;

Dieser Code ist „sicherer“, aber immer noch nicht Thread-sicher. Es gibt keine Garantie dafür, dass der _totalAmount mit den Einträgen im Wörterbuch konsistent ist. Ein anderer Thread kann versuchen, diese Werte zu lesen, ein UI-Element zu aktualisieren:

Decimal totalAmount;
lock (_locker) totalAmount = _totalAmount;
UpdateUI(_dict.Count, totalAmount);

Die totalAmount kann nicht auf die Anzahl der Aufträge im Wörterbuch entsprechen. Die angezeigten Statistiken könnte falsch sein. An dieser Stelle werden Sie feststellen, dass Sie den lock Schutz verlängern müssen die Aktualisierung des Wörterbuchs enthalten:

// Thread A
lock (_locker)
{
    _dict.TryAdd(order.Id, order);
    _totalAmount += order.Amount;
}

// Thread B
int ordersCount;
Decimal totalAmount;
lock (_locker)
{
    ordersCount = _dict.Count;
    totalAmount = _totalAmount;
}
UpdateUI(ordersCount, totalAmount);

Dieser Code ist vollkommen sicher, aber alle Vorteile eines ConcurrentDictionary mit verschwunden sind.

  1. Die Leistung hat sich schlimmer als ein normales Dictionary verwenden, da die interne Verriegelung im Inneren des ConcurrentDictionary jetzt verschwenderisch und redundant ist.
  2. Sie müssen den gesamten Code für ungeschützte Anwendungen der gemeinsam genutzten Variablen überprüfen.
  3. Sie sind mit der Verwendung eine peinliche API stecken (TryAdd ?, AddOrUpdate?).

Also mein Rat ist: Beginnen Sie mit einem Dictionary + lock, und halten Sie die Möglichkeit, ein Upgrade später zu einer ConcurrentDictionary als Performance-Optimierung, wenn diese Option tatsächlich lebensfähig ist. In vielen Fällen wird es nicht sein.

Lizenziert unter: CC-BY-SA mit Zuschreibung
Nicht verbunden mit StackOverflow
scroll top