3 Stimmen

Teilweise thread-sicheres Wörterbuch

Ich habe eine Klasse, die eine private Dictionary Instanz, die einige Daten zwischenspeichert.

Die Klasse schreibt in das Wörterbuch von mehreren Threads aus unter Verwendung eines ReaderWriterLockSlim .

Ich möchte die Werte des Wörterbuchs außerhalb der Klasse sichtbar machen.
Wie kann man das thread-sicher machen?

Im Moment habe ich Folgendes:

public ReadOnlyCollection<MyClass> Values() {
    using (sync.ReadLock())
        return new ReadOnlyCollection<MyClass>(cache.Values.ToArray()); 
}

Gibt es eine Möglichkeit, dies zu tun, ohne die Sammlung viele Male zu kopieren?

Ich verwende .Net 3.5 (nicht 4.0)

9voto

Eric Lippert Punkte 628543

Ich möchte die Werte des Wörterbuchs außerhalb der Klasse freigeben. Was ist ein Thread-sicherer Weg, dies zu tun?

Sie haben drei Möglichkeiten.

1) Machen Sie eine Kopie der Daten und händigen Sie die Kopie aus. Vorteile: keine Sorgen über den thread-sicheren Zugriff auf die Daten. Nachteile: Der Kunde erhält eine Kopie veralteter Daten, keine frischen, aktuellen Daten. Außerdem ist das Kopieren teuer.

2) Geben Sie ein Objekt aus, das die zugrundeliegende Sammlung sperrt, wenn aus ihr gelesen wird. Sie müssen Ihre eigene schreibgeschützte Sammlung schreiben, die einen Verweis auf die Sperre der "übergeordneten" Sammlung hat. Entwerfen Sie beide Objekte sorgfältig, so dass Deadlocks unmöglich sind. Vorteile: Aus Sicht des Kunden "funktioniert" es einfach; er erhält aktuelle Daten, ohne sich um Sperren kümmern zu müssen. Nachteile: Mehr Arbeit für Sie.

3) Verweisen Sie das Problem an den Kunden. Legen Sie die Sperre offen und machen Sie es zur Bedingung, dass Clients alle Ansichten auf die Daten selbst sperren, bevor sie sie verwenden. Vorteile: Keine Arbeit für Sie. Nachteile: Viel mehr Arbeit für den Kunden, Arbeit, die er möglicherweise nicht leisten will oder kann. Das Risiko von Deadlocks usw. ist nun das Problem des Kunden und nicht mehr Ihr Problem.

2voto

Sam Harwell Punkte 94511

Wenn Sie eine Schnappschuss des aktuellen Zustands des Wörterbuchs gibt es eigentlich nichts, was man mit diesem Sammlungstyp tun könnte. Dies ist die gleiche Technik, die von der ConcurrentDictionary<TKey, TValue>.Values Eigentum.

Wenn es Ihnen nichts ausmacht, eine InvalidOperationException wenn die Sammlung während der Aufzählung geändert wird, könnten Sie einfach zurückgeben cache.Values da sie schreibgeschützt ist (und somit die Wörterbuchdaten nicht beschädigen kann).

1voto

Dan Tao Punkte 121990

エディテージ : Ich persönlich glaube, dass der unten stehende Code Ihre Frage technisch korrekt beantwortet (d.h. er bietet eine Möglichkeit, die Werte in einer Sammlung aufzuzählen, ohne eine Kopie zu erstellen). Einige Entwickler, die weitaus seriöser sind als ich, raten dringend gegen diesen Ansatz aus Gründen, die sie in ihren Bearbeitungen/Kommentaren erläutert haben. Kurz gesagt: Das ist offensichtlich eine schlechte Idee. Deshalb lasse ich die Antwort stehen, schlage aber vor, dass Sie sie nicht verwenden.


Wenn ich nichts übersehe, glaube ich, dass Sie Ihre Werte als eine IEnumerable<MyClass> ohne dass Werte kopiert werden müssen, indem man die yield Stichwort:

public IEnumerable<MyClass> Values {
    get {
        using (sync.ReadLock()) {
            foreach (MyClass value in cache.Values)
                yield return value;
        }
    }
}

Beachten Sie jedoch (und ich nehme an, dass Sie das bereits wussten), dass dieser Ansatz faule Bewertung was bedeutet, dass die Values Eigenschaft, wie oben implementiert, kann no so behandelt werden, als ob sie eine Schnappschuss .

Mit anderen Worten... nun, sehen Sie sich diesen Code an (ich vermute natürlich einige Details dieser Klasse von Ihnen):

var d = new ThreadSafeDictionary<string, string>();

// d is empty right now
IEnumerable<string> values = d.Values;

d.Add("someKey", "someValue");

// if values were a snapshot, this would output nothing...
// but in FACT, since it is lazily evaluated, it will now have
// what is CURRENTLY in d.Values ("someValue")
foreach (string s in values) {
    Console.WriteLine(s);
}

Wenn es also eine Voraussetzung ist, dass diese Values Eigenschaft gleichwertig sein mit einer Schnappschuss von dem, was in cache zum Zeitpunkt des Zugriffs auf die Immobilie, dann müssen Sie eine Kopie machen.

(Beginn 280Z28): Im Folgenden finden Sie ein Beispiel dafür, wie jemand, der mit der "C#-Methode" nicht vertraut ist, den Code sperren könnte:

IEnumerator enumerator = obj.Values.GetEnumerator();
MyClass first = null;
if (enumerator.MoveNext())
    first = enumerator.Current;

(Ende 280Z28)

0voto

Dewfy Punkte 22558

Überprüfen Sie die nächste Möglichkeit, stellt nur ICollection-Schnittstelle, so in Werte() können Sie Ihre eigene Implementierung zurückgeben. Diese Implementierung wird nur Verweise auf Dictioanry.Values verwenden und immer ReadLock für den Zugriff auf Elemente verwenden.

CodeJaeger.com

CodeJaeger ist eine Gemeinschaft für Programmierer, die täglich Hilfe erhalten..
Wir haben viele Inhalte, und Sie können auch Ihre eigenen Fragen stellen oder die Fragen anderer Leute lösen.

Powered by:

X