2009-08-24 7 views
1

Ich habe eine statische Sammlungen, die IList-Schnittstelle implementiert. Diese Sammlung wird in der gesamten Anwendung verwendet, einschließlich Hinzufügen/Entfernen von Elementen.wie Sperre auf einem IList verwenden

Aufgrund Multithread-Problem, ich frage mich, was ich tun kann, um sicherzustellen, dass die Liste nacheinander ändert, wie wenn 1 Thread versuchen, ein Element hinzuzufügen, sollte ein anderer Thread Element zu diesem Zeitpunkt nicht löschen.

Ich frage mich, was ist der Unterschied zwischen lock (this) und lock (privateObject)? Welcher ist in meinem Fall besser?

Vielen Dank.

+0

Intelligente Verriegelung auf einer Sammlung ist nicht ganz geradlinig. Jared Par hat einen guten [Blogbeitrag] (http://blogs.msdn.com/jaredpar/archive/2009/02/11/why-are-thread-safe-collections-so-hard.aspx) den Sie wahrscheinlich lesen sollten . – ScottS

Antwort

4

Die wird für die gesamte Instanz sperren, während lock(privateObject) nur diese spezifische Instanzvariable sperren wird. Die zweite Option ist die bessere Wahl, da das Sperren der gesamten Instanz verhindert, dass andere Threads mit dem Objekt etwas anfangen können.

Von MSDN:

Im Allgemeinen vermeidet auf einer Art öffentliche Sperren oder Instanzen außerhalb Ihres Steuercode. Die gemeinsamen Konstrukte sperren (this), Schloss (typeof (MyType)) und Sperre ("myLock") verletzen diese Richtlinie:

  • Schloss (das) ist ein Problem, wenn die Instanz sein kann, öffentlich zugänglich

  • Sperre (typeof (MyType)) ist ein Problem, wenn MyType öffentlich zugänglich ist.

  • Sperre („myLock“) ist ein Problem, da jeder anderer Code in dem Prozess die gleiche Zeichenfolge verwendet wird, wird die gleichen Verriegelungs teilen.

Best Practice ist ein privaten Objekt zu definieren zu erfassen oder eine private static Objektvariable, um Daten gemeinsam für alle Instanzen zu schützen.

In diesem speziellen Fall ist die Sammlung statisch, die es effektiv bedeutet, ist eine Instanz, aber das ändert sich noch nicht, wie die lock(this) und lock(privateObject) verhalten würden.

Durch die Verwendung von sogar in einer statischen Sammlung sperren Sie immer noch die gesamte Instanz. In diesem Fall müssen alle anderen Threads warten, bis Thread A eine Sperre für Methode Foo() erwirbt, um die Operation für die Sammlung auszuführen.

lock(privateObject) Verwendung bedeutet, dass sobald Thread A eine Sperre für Verfahren erwirbt Foo() alle andere Threads eines beliebigen anderen Betrieb außer Foo() ohne zu warten, durchführen können. Nur wenn ein anderer Thread versucht, die Methode Foo() auszuführen, muss er warten, bis Thread A seine Foo() Operation abgeschlossen und die Sperre aufgehoben hat.

+0

meine Sammlung ist statisch, ist es wahr, dass sie alle nur eine Instanz in meiner Anwendung sind? also beide Schlösser gleich? – thethanghn

+0

Ja, eine statische Sammlung bedeutet, dass es tatsächlich eine einzelne Instanz in der Anwendung gibt, aber das ändert nichts daran, wie Sie sperren. Ich habe meine Antwort aktualisiert, um dies zu beheben. –

1

Das Schlüsselwort lock ist ein wenig verwirrend. Der Objektausdruck in der lock-Anweisung ist in Wirklichkeit nur ein Identifizierungsmechanismus zum Erstellen kritischer Abschnitte. Es ist nicht das Thema der Sperre, und es ist in keinster Weise garantiert, dass es für Multithread-Operationen sicher ist, nur weil es von der Anweisung referenziert wird.

So erstellt lock(this) einen kritischen Abschnitt, der von der Klasse identifiziert wird, die die aktuell ausgeführte Methode enthält, während lock(privateObject) durch ein Objekt identifiziert wird, das (vermutlich sowieso) privat für die Klasse ist. Ersteres ist risikoreicher, da ein Aufrufer Ihrer Klasse unbeabsichtigt ihre eigenen kritischen Abschnitte definieren könnte, indem sie eine lock-Anweisung verwendet, die diese Klasseninstanz als Sperrobjekt verwendet. Dies könnte zu unbeabsichtigten Threading-Problemen führen, einschließlich, aber nicht beschränkt auf Deadlocks und Engpässe.

Sie haben erwähnt, dass Sie sich mit mehreren Threads beschäftigt haben, die die Sammlung gleichzeitig ändern. Ich sollte darauf hinweisen, dass Sie sich gleichermaßen mit Threads beschäftigen sollten, die die Sammlung lesen, auch wenn sie diese nicht verändern. Es ist wahrscheinlich, dass Sie einige der gleichen Schutzvorrichtungen benötigen, um die Sammlung während der Lesevorgänge so zu schützen, wie Sie dies während der Schreibvorgänge tun würden.

1

Fügen Sie der Klasse, für die Methoden gesperrt werden, ein privates Mitglied hinzu.

z.

public class MyClass : IList 
{ 
    private object syncRoot = new object(); 

    public void Add(object value) 
    { 
     lock(this.syncRoot) 
     { 
      // Add code here 
     } 
    } 

    public void Remove(object value) 
    { 
     lock(this.syncRoot) 
     { 
      // Remove code here 
     } 
    } 
} 

Dies wird, dass der Zugriff auf die Liste sicherzustellen, zwischen Threads für die Hinzufügen und Entfernen von Fällen synchronisiert, während der Zugang zu der Liste zu halten. Auf diese Weise können Enumeratoren weiterhin auf die Liste zugreifen, während ein anderer Thread sie ändern kann. Dies führt jedoch zu einem weiteren Problem, bei dem ein Enumerator eine Ausnahme auslöst, wenn die Auflistung während der Enumeration geändert wird.