Verwenden der Sperre für den Schlüssel eines Dictionarystring-Objekts

8

Ich habe ein Dictionary<string, someobject> .

EDIT: Es wurde darauf hingewiesen, dass mein Beispiel schlecht war. Meine ganze Absicht war nicht, die Referenzen in einer Schleife zu aktualisieren, sondern um verschiedene Werte zu aktualisieren, die auf unterschiedlichen Threads basieren, müssen die Daten aktualisieren / abrufen. Ich habe die Schleife in eine Methode geändert.

Ich muss Elemente in meinem Wörterbuch aktualisieren - ein Schlüssel nach dem anderen und ich frage mich, ob es Probleme bei der Verwendung der Sperre für den .key-Wert meines Dictionary-Objekts gibt?

%Vor%

Würde das vor Gericht bestehen oder scheitern? Ich möchte nur, dass jeder Wert im Wörterbuch unabhängig gesperrt wird, sodass das Sperren (und Aktualisieren) eines Wertes die anderen nicht sperrt. Ich bin mir auch bewusst, dass die Sperre für eine lange Zeit hält - aber die Daten werden ungültig, bis sie vollständig aktualisiert werden.

    
Per Hornshøj-Schierbeck 01.10.2008, 13:12
quelle

7 Antworten

11

Das Sperren eines Objekts, auf das außerhalb der Code-Sperre zugegriffen werden kann, ist ein großes Risiko. Wenn irgendein anderer Code (irgendwo) jemals dieses Objekt sperrt, könnte es zu einigen Deadlocks kommen, die schwer zu debuggen sind. Beachten Sie auch, dass Sie das Objekt sperren, nicht die Referenz. Wenn ich Ihnen also ein Wörterbuch gab, kann ich immer noch Referenzen auf die Schlüssel haben und sie sperren - wodurch wir das gleiche Objekt sperren / p>

Wenn Sie das Wörterbuch vollständig einkapseln und die Schlüssel selbst generieren (sie werden niemals übergeben, sind Sie sicher.)

Versuchen Sie jedoch, bei einer Regel zu bleiben - schränken Sie die Sichtbarkeit der Objekte, die Sie sperren, nach Möglichkeit auf den Sperrcode ein.

Deshalb siehst du das:

%Vor%

anstatt die obige Zeile A durch

zu ersetzen %Vor%

Auf diese Weise ist ein separates Objekt gesperrt und die Sichtbarkeit ist begrenzt.

Bearbeiten Jon Skeet hat korrekt festgestellt, dass lockObj oben nur gelesen werden sollte.

    
Philip Rieck 01.10.2008, 13:23
quelle
8

Nein, das würde nicht funktionieren.

Der Grund ist String Interning . Das bedeutet:

%Vor%

sind beide das gleiche Objekt! Daher sollten Sie keine Strings sperren, da Sie, wenn ein anderer Teil des Programms (z. B. eine andere Instanz desselben Objekts) ebenfalls dieselbe Zeichenfolge sperren möchte, versehentlich Sperrkonflikte erstellen könnten, wo dies nicht erforderlich ist. möglicherweise sogar eine Sackgasse.

Fühlen Sie sich frei, dies mit Nicht-Strings zu tun. Für die beste Klarheit mache ich es mir zur Gewohnheit, immer ein separates Sperrobjekt zu erstellen:

%Vor%

Ich empfehle Ihnen, das Gleiche zu tun. Erstellen Sie ein Dictionary mit den Sperrobjekten für jede Matrixzelle. Sperren Sie diese Objekte dann bei Bedarf.

PS. Das Ändern der Sammlung, die Sie durchlaufen, wird nicht als sehr gut angesehen. Es wird sogar eine Ausnahme mit den meisten Sammlungstypen auslösen. Versuchen Sie, dies neu zu gestalten - z. Iteriere über eine Liste von Schlüsseln, wenn sie immer konstant ist, nicht die Paare.

    
Sander 01.10.2008 13:18
quelle
3

Hinweis: Ich nehme eine Ausnahme an, wenn das Ändern der Sammlung während der Iteration bereits behoben ist

Das Dictionary ist keine thread-sichere Sammlung, dh es ist nicht sicher, die Sammlung von verschiedenen Threads ohne externe Synchronisation zu ändern und zu lesen. Hashtable ist (war?) Thread-sicher für ein Schreiber-viele-Leser-Szenario, aber Dictionary hat andere interne Datenstruktur und erbt diese Garantie nicht.

Das bedeutet, dass Sie Ihr Wörterbuch nicht ändern können, während Sie darauf zugreifen, um es vom anderen Thread zu lesen oder zu schreiben, es kann nur interne Datenstrukturen zerstören. Durch das Sperren des Schlüssels wird die interne Datenstruktur nicht geschützt, denn während Sie diesen Schlüssel ändern, könnte jemand einen anderen Schlüssel Ihres Wörterbuchs in einem anderen Thread lesen. Selbst wenn Sie garantieren können, dass alle Ihre Schlüssel die gleichen Objekte sind (wie beim String-Interning), bringt Sie dies nicht auf die sichere Seite. Beispiel:

  1. Sie sperren den Schlüssel und beginnen mit dem Ändern des Wörterbuchs
  2. Ein anderer Thread versucht, Wert für den Schlüssel zu erhalten, der zufällig in denselben Bucket fällt wie der gesperrte. Dies ist nicht nur der Fall, wenn die Hashcodes zweier Objekte gleich sind, sondern häufiger, wenn der Hashcode% tableSize gleich ist.
  3. Beide Threads greifen auf denselben Bucket zu (verknüpfte Liste von Schlüsseln mit demselben Hashcode% tableSize-Wert)

Wenn im Wörterbuch kein solcher Schlüssel vorhanden ist, beginnt der erste Thread mit dem Ändern der Liste, und der zweite Thread wird wahrscheinlich den unvollständigen Status lesen.

Wenn ein solcher Schlüssel bereits vorhanden ist, können Implementierungsdetails des Wörterbuchs die Datenstruktur noch ändern, z. B. können zuletzt aufgerufene Schlüssel zum schnelleren Abrufen an den Anfang der Liste verschoben werden. Sie können sich nicht auf Implementierungsdetails verlassen.

Es gibt viele Fälle wie diese, wenn Sie das Wörterbuch beschädigt haben. Sie müssen also ein externes Synchronisationsobjekt haben (oder das Dictionary selbst verwenden, wenn es nicht öffentlich zugänglich ist) und es während des gesamten Vorgangs sperren. Wenn Sie mehr granulare Sperren benötigen, wenn die Operation einige Zeit in Anspruch nehmen kann, können Sie die zu aktualisierenden Schlüssel kopieren, darüber iterieren, das gesamte Wörterbuch während der Einzelschlüsselaktualisierung sperren (vergessen Sie nicht, den Schlüssel noch immer zu überprüfen) und freigeben Lass andere Threads laufen.

    
Ilya Ryzhenkov 01.10.2008 16:02
quelle
2

Wenn ich mich nicht irre, bestand die ursprüngliche Absicht darin, ein einzelnes Element zu sperren, anstatt das gesamte Wörterbuch zu sperren (wie Sperren auf Tabellenebene oder Sperren auf Zeilenebene in einem DB)

Sie können den Schlüssel des Wörterbuchs nicht so wie hier erläutert sperren.

Was Sie tun können, ist, ein internes Wörterbuch von Sperrobjekten zu führen, das dem tatsächlichen Wörterbuch entspricht. Wenn Sie also in Ihr YourDictionary [Key1] schreiben möchten, sperren Sie zunächst InternalLocksDictionary [Key1] - also schreibt nur ein einzelner Thread in YourDictionary.

Ein (nicht allzu sauberes) Beispiel kann hier gefunden werden .

    
Ken Egozi 11.02.2009 07:45
quelle
0

In Ihrem Beispiel können Sie nicht tun, was Sie tun möchten!

Sie erhalten eine System.InvalidOperationException mit der Nachricht Sammlung wurde geändert. Aufzählungsoperation kann nicht ausgeführt werden.

Hier ist ein Beispiel zum Beweis:

%Vor%

Die Ausgabe wäre:

%Vor%     
Ray Hayes 01.10.2008 13:24
quelle
0

Ich kann dort einige mögliche Probleme sehen:

  1. Strings können geteilt werden, so dass Sie nicht unbedingt wissen, wer sonst aus welchem ​​Grund auch immer das Schlüsselobjekt sperrt
  2. Zeichenketten können nicht geteilt werden: Sie können einen Zeichenkettenschlüssel mit dem Wert "Key1" sperren und ein anderer Teil eines Codes kann ein anderes Zeichenkettenobjekt haben, das auch die Zeichen "Key1" enthält . Für das Wörterbuch sind sie derselbe Schlüssel, aber was das Sperren betrifft, sind sie verschiedene Objekte.
  3. Diese Sperre verhindert nicht Änderungen an den Wertobjekten selbst, d. h. matrixElements[someKey].ChangeAllYourContents()
babackman 01.10.2008 13:31
quelle
0

Ich bin gerade auf dieses Thema gestoßen und habe mir Gedanken über Code geschrieben, den ich vor ein paar Jahren geschrieben habe, wo ich auf der Basis eines Wörterbuchs gebraucht habe

%Vor%

die Sperrklasse

%Vor%     
Mark 14.04.2016 11:11
quelle

Tags und Links