Frage

Ich habe eine Dictionary<string, someobject>.

EDIT:Es wurde mir darauf hingewiesen, dass mein Beispiel war schlecht.Meine ganze Absicht war nicht, aktualisieren Sie die Referenzen in einer Schleife, update verschiedene Werte basierend auf verschiedene threads aktualisieren/abrufen der Daten.Ich habe die Schleife auf eine Methode.

Ich brauche, um Elemente zu aktualisieren, die in meinem Wörterbuch - one-key-zu einer Zeit, und ich Frage mich, ob es irgendwelche Probleme bei der Verwendung der Sperre für die .key-value meiner Dictionary-Objekt?

private static Dictionary<string, MatrixElement> matrixElements = new Dictionary<string, MatrixElement>();

//Pseudo-code

public static void UpdateValue(string key)
{
    KeyValuePair<string, MatrixElement> keyValuePair = matrixElements[key];
    lock (keyValuePair.Key)
    {
        keyValuePair.Value  = SomeMeanMethod();
    }
}

Würde, dass die vor Gericht oder nicht?Ich will einfach nur den jeweiligen Wert in das Wörterbuch gesperrt werden unabhängig voneinander so sperren (und Aktualisierung) einen Wert verriegelt nicht die anderen.Auch ich bin mir bewusst, dass die Verriegelung wird halten für eine lange Zeit - aber die Daten werden ungültig, bis vollständig aktualisiert.

War es hilfreich?

Lösung

auf ein Objekt sperren, die außerhalb des Codes zugänglich ist es Sperren ist ein großes Risiko. Wenn ein anderer Code (überall) jemals Sperren, die Sie widersprechen könnte in einigen Deadlocks, die zu debuggen hart sind. Beachten Sie auch, dass Sie die Objekt , nicht die Referenz zu sperren, so dass, wenn ich Ihnen ein Wörterbuch gab, habe ich noch Verweise auf die Tasten halten können und Sperre für sie - was uns auf dem gleichen Objekt sperren <. / p>

Wenn Sie das Wörterbuch vollständig einzukapseln, und erzeugen die Schlüssel selbst (sie sind nicht immer übergeben, dann können Sie sicher sein.

Allerdings versuchen, eine Regel zu bleiben -. Die Sichtbarkeit der Objekte beschränken Sie den Sperrcode sperren sich selbst, wenn möglich

Das ist, warum Sie dies sehen:

public class Something
{
  private readonly object lockObj = new object();

  public SomethingReentrant()
  {
    lock(lockObj)    // Line A
    {
      // ...
     }
   }
}

anstatt der Linie A Sehen oben ersetzt durch

  lock(this)

Auf diese Weise wird ein separates Objekt ist gesperrt, auf, und die Sicht ist eingeschränkt.

Bearbeiten Jon Skeet richtig beobachtet, dass lockObj oben sollte nur lesbar sein.

Andere Tipps

Nein, das würde nicht funktionieren.

Der Grund dafür ist Zeichenfolge Internierung . Dies bedeutet, dass:

string a = "Something";
string b = "Something";

sind beide das gleiche Objekt! Daher sollten Sie nie, weil auf Strings sperren, wenn ein anderer Teil des Programms (zum Beispiel eine andere Instanz desselben Objekts) auch auf derselben Saite sperren will, versehentlich Sperrenkonflikte schaffen könnte, wo es nicht nötig ist; möglicherweise sogar ein Deadlock.

Sie können ferner diese mit nicht-Strings zu tun, wenn. Für die beste Klarheit ich es mache eine persönliche Gewohnheit, immer ein separates Sperrobjekt zu erstellen:

class Something
{
    bool threadSafeBool = true;
    object threadSafeBoolLock = new object(); // Always lock this to use threadSafeBool
}

Ich empfehle Ihnen, das gleiche zu tun. Erstellen Sie ein Wörterbuch mit den Sperrobjekte für jede Matrixzelle. Dann diese Objekte sperren, wenn nötig.

PS. Ändern der Sammlung die Sie iterieren ist nicht sehr schön betrachtet. Es wird sogar eine Ausnahme bei den meisten Sammlungstypen werfen. Versuchen Sie, diese Refactoring - z.B. eine Liste der Schlüssel iterieren, wenn es immer konstant sein wird, nicht die Paare.

Hinweis: Ich gehe davon aus, wenn Ausnahme Sammlung während der Iteration zu modifizieren bereits festgelegt ist

Wörterbuch ist nicht Thread-sicher-Sammlung, das heißt, es nicht sichere Sammlung von verschiedenen Threads ohne externe Synchronisation zu ändern und zu lesen. Hashtable ist (war?) Thread-sicher für ein Schriftsteller-many-Leser Szenario, aber Dictionary hat verschiedene interne Datenstruktur und stellt diese Garantie nicht erben.

Das bedeutet, dass Sie Ihr Wörterbuch nicht ändern können, während Sie es lesen oder schreiben aus dem anderen Thread zugreifen, können sie brach nur interne Datenstrukturen. Sperren auf dem Schlüssel nicht schützt interne Datenstruktur, denn während Sie, dass sehr Schlüssel jemand ändern könnte verschiedene Schlüssel Ihres Wörterbuch in einem anderen Thread lesen. Auch wenn Sie garantieren können, dass alle Ihre Schlüssel sind gleiche Objekte (wie die über Zeichenfolge Internierung), bedeutet dies nicht auf sicherer Seite bringen. Beispiel:

  1. Sie sperren den Schlüssel und beginnen Wörterbuch
  2. ändern
  3. Ein anderer Thread versucht, Wert für den Schlüssel zu erhalten, die in den gleichen Eimer wie verriegelt ein fallen passiert. Dies ist nicht nur, wenn Hashcodes zweier Objekte gleich sind, aber häufiger auf, wenn hashcode% table ist die gleiche.
  4. beide Threads greifen auf den gleichen Eimern (verkettete Liste von Tasten mit gleichen Hashcode% tableWert)

Wenn es kein solcher Schlüssel im Wörterbuch ist, wird zuerst Thread startet die Liste ändern, und der zweite Thread wird wahrscheinlich unvollständig Zustand zu lesen.

Wenn ein solcher Schlüssel bereits vorhanden ist, Details der Implementierung Wörterbuch noch könnten Datenstruktur, zum Beispiel ändern schnelleren Abruf zuletzt zugegriffen Schlüssel auf den Kopf der Liste verschieben. Sie können nicht auf Implementierungsdetails verlassen.

Es gibt viele Fälle so, wenn Sie Wörterbuch beschädigt haben werden. So müssen Sie externe Synchronisationsobjekt (oder verwenden Wörterbuch selbst, wenn es nicht für die Öffentlichkeit ausgesetzt ist) und Sperre für sie während der gesamten Operation. Wenn Sie mehr granulare Sperren benötigen, wenn der Betrieb eine gewisse Zeit in Anspruch nehmen kann, können Sie Schlüssel kopieren Sie aktualisieren müssen, durchlaufen sie, sperren gesamte Wörterbuch während der Einzel Key-Update (nicht vergessen, Schlüssel, um zu überprüfen ist immer noch da) und lassen Sie ihn an lassen Sie andere Threads laufen.

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

Sie können nicht auf den Schlüssel des Wörterbuch sperren so viele hier erklärt.

Was Sie tun können, ist ein internes Wörterbuch von Sperrobjekten zu halten, die den eigentlichen Wörterbuch entspricht. Also, wenn Sie zu Yourdictionary [Key1] schreiben, würde wollen, müssen Sie zuerst sperren auf InternalLocksDictionary [Key1.] - so nur ein einziger Thread schreiben, wird Yourdictionary

a (nicht zu sauber) Beispiel kann sein hier .

kam gerade über diese und dachte id Aktie einige Code, den ich vor ein paar Jahren schrieb, wo ich ein Wörterbuch auf einem Schlüssel Basis erforderlich

 using (var lockObject = new Lock(hashedCacheID))
 {
    var lockedKey = lockObject.GetLock();
    //now do something with the dictionary
 }

die Schlossklasse

class Lock : IDisposable
    {
        private static readonly Dictionary<string, string> Lockedkeys = new Dictionary<string, string>();

        private static readonly object CritialLock = new object();

        private readonly string _key;
        private bool _isLocked;

        public Lock(string key)
        {
            _key = key;

            lock (CritialLock)
            {
                //if the dictionary doesnt contain the key add it
                if (!Lockedkeys.ContainsKey(key))
                {
                    Lockedkeys.Add(key, String.Copy(key)); //enusre that the two objects have different references
                }
            }
        }

        public string GetLock()
        {
            var key = Lockedkeys[_key];

            if (!_isLocked)
            {
                Monitor.Enter(key);
            }
            _isLocked = true;

            return key;
        }

        public void Dispose()
        {
            var key = Lockedkeys[_key];

            if (_isLocked)
            {
                Monitor.Exit(key);
            }
            _isLocked = false;
        }
    }

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

Sie werden ein erhalten System.InvalidOperationException mit einer Nachricht von Die Auflistung wurde geändert; Aufzählung kann möglicherweise nicht ausgeführt.

Hier ist ein Beispiel zu beweisen:

using System.Collections.Generic;
using System;

public class Test
{
    private Int32 age = 42;

    static public void Main()
    {
       (new Test()).TestMethod();
    }

    public void TestMethod()
    {
        Dictionary<Int32, string> myDict = new Dictionary<Int32, string>();

        myDict[age] = age.ToString();

        foreach(KeyValuePair<Int32, string> pair in myDict)
        {
            Console.WriteLine("{0} : {1}", pair.Key, pair.Value);
            ++age;
            Console.WriteLine("{0} : {1}", pair.Key, pair.Value);
            myDict[pair.Key] = "new";
            Console.WriteLine("Changed!");
        }
    }   
}

Der Ausgang wäre:

42 : 42
42 : 42

Unhandled Exception: System.InvalidOperationException: Collection was modified; enumeration operation may not execute.
   at System.ThrowHelper.ThrowInvalidOperationException(ExceptionResource resource)
   at System.Collections.Generic.Dictionary`2.Enumerator.MoveNext()
   at Test.TestMethod()
   at Test.Main()

Ich kann sehen, ein paar potentielle Probleme gibt:

  1. strings können geteilt werden, so dass Sie nicht unbedingt wissen, wer sonst könnten Verriegelung auf das key-Objekt für das, was anderen Grund
  2. strings können nicht geteilt werden:Sie können sperren auf einem string-Schlüssel mit dem Wert "Key1" und einige andere Stück code, das vielleicht ein anderes string-Objekt, das enthält auch die Zeichen "Key1".Dem Wörterbuch sind Sie die gleiche Taste, aber so weit wie die Verriegelung ist besorgt, Sie werden verschiedene Objekte.
  3. Dass die Verriegelung nicht verhindern, dass änderungen des Wert-Objekte selbst, also matrixElements[someKey].ChangeAllYourContents()
Lizenziert unter: CC-BY-SA mit Zuschreibung
Nicht verbunden mit StackOverflow
scroll top