2009-04-01 6 views
42

von der folgenden Website:Warum ist die Liste <T> nicht Thread-sicher?

http://crfdesign.net/programming/top-10-differences-between-java-and-c

Leider List<> ist nicht Thread-sicher (C#‘s ArrayList und Java Vector sind Thread-sicher). C# hat auch eine Hashtable; die generische Version ist:

Was List<T> nicht Thread-sicher macht? Ist es ein Implementierungsproblem beim .NET Framework Engineer? Oder sind Generika nicht threadsicher?

+0

Nach MSDN verwenden [Array] (http://msdn.microsoft.com/en-us/library/system.collections. arraylist.aspx # threadSafetyToggle) und [List (Of T)] (http://msdn.microsoft.com/en-us/library/6sh2ey19.aspx#threadSafetyToggle) haben dieselbe Threadsicherheit. 'ArrayList' bietet jedoch den Wrapper [Synchronized] (http://msdn.microsoft.com/en-us/library/system.collections.arraylist.synchronized.aspx). –

Antwort

67

Sie müssen Java's Vector-Typ der Threadsicherheit wirklich klassifizieren. Javas Vector kann sicher aus mehreren Threads verwendet werden, da es die Synchronisierung der Methoden verwendet. Der Status wird nicht beschädigt.

Die Verwendbarkeit des Java-Vektors ist jedoch von mehreren Threads ohne zusätzliche Synchronisation beschränkt. Betrachten wir zum Beispiel die einfache Akt der ein Element aus einem Vektor

Vector vector = getVector(); 
if (vector.size() > 0) { 
    object first = vector.get(0); 
} 

Diese Methode wird nicht der Staat korrupt des Vektors, aber es ist auch nicht richtig zu lesen. Es gibt nichts, was einen anderen Thread davon abhält, den Vektor zwischen der if-Anweisung und dem Aufruf get() zu mutieren. Dieser Code kann und wird schließlich wegen einer Race-Bedingung fehlschlagen.

Diese Art der Synchronisation ist nur in einer handvoll Szenarien nützlich und es ist sicherlich nicht billig. Sie zahlen einen bemerkenswerten Preis für die Synchronisierung, auch wenn Sie nicht mehrere Threads verwenden.

.Net hat diesen Preis nicht standardmäßig für ein Szenario mit nur begrenzter Verwendbarkeit gewählt. Stattdessen entschied er sich, eine lockfreie Liste zu implementieren. Autoren sind dafür verantwortlich, eine Synchronisierung hinzuzufügen. Es ist näher an C++ - Modell von "nur für das, was Sie verwenden"

Ich schrieb vor kurzem ein paar Artikel über die Gefahren der Verwendung von Sammlungen mit nur interner Synchronisation wie Java-Vektor.

Referenz Vector Thread-Sicherheit: http://www.ibm.com/developerworks/java/library/j-jtp09263.html

+13

Kleine Anmerkung: "frei sperren" bedeutet "synchronisiert ohne Sperren", nicht "keine Sperren". http://en.wikipedia.org/wiki/Lock_free –

+0

Ich stimme Strilanc zu: Ich musste die Terminologie "lock free" doppelt verwenden. –

+0

Methoden wie "Count" können vollkommen vernünftig und legitim auf einer Thread-sicheren Sammlung verwendet werden, um zu entscheiden, sich nicht mit etwas zu beschäftigen. Wenn zum Beispiel eine Operation mit zwei aus einer Warteschlange entnommenen Elementen ausgeführt werden muss, kann es durchaus sinnvoll sein, eine Eigenschaft zu verwenden, um die Anzahl zu überprüfen (mit einer Lesebarriere statt einer Sperre) und wenn die Anzahl ausreichend ist , erhalte das Schloss und überprüfe die Zählung erneut. Wenn es immer noch ausreicht, führen Sie den Vorgang aus; sonst überspringen Sie es. Lassen Sie die Sperre auf jeden Fall los. – supercat

20

Warum wäre es Thread-sicher sein? Nicht jede Klasse ist. In der Tat sind Klassen standardmäßig nicht thread-safe.

Wenn threadsicher ist, bedeutet dies, dass jeder Vorgang, der die Liste ändert, gegen den gleichzeitigen Zugriff gesperrt werden muss. Dies wäre sogar für die Listen notwendig, die nur von einem einzigen Thread verwendet werden. Das wäre sehr ineffizient.

9

Es ist einfach eine Design-Entscheidung, die Arten zu implementieren nicht Thread-sicher. Die Sammlungen stellen die Eigenschaft SyncRoot der Schnittstelle ICollection und die Synchronized()-Methode für einige Sammlungen zum expliziten Synchronisieren der Datentypen bereit.

Verwenden Sie SyncRoot, um ein Objekt in Multithread-Umgebungen zu sperren.

lock (collection.SyncRoot) 
{ 
    DoSomething(collection); 
} 

Verwenden collection.Synchronized() einen Thread-safe-Wrapper für die Sammlung zu erhalten.

+4

In den Artikeln von JaredPar finden Sie eine Erklärung, warum das mit ziemlicher Sicherheit nicht das ist, was Sie tun möchten. –

2

Für echte Fadensicherheit müssen List<> und andere Sammlungstypen unveränderlich sein. Mit den parallelen Erweiterungen von .NET, die in .NET 4.0 herauskommen, werden wir thread-sichere Versionen der am häufigsten verwendeten Sammlungen sehen. Jon Skeet berührt etwas davon.

2

Die Race-Condition-Möglichkeit, die JaredPar erwähnt, ist die beängstigende Konsequenz, sich auf Vectors vermeintliche Thread-Sicherheit zu verlassen. Es ist die Art von Sache, die dazu führt, dass "jeden neunten Dienstag die App etwas Seltsames tut" -Sort des Fehlerberichtes, der dich verrückt macht.

Es gibt eine Reihe von wirklich Thread-sicheren Sammlungen coming in .Net 4, mit einem interessanten Nebeneffekt, dass sie Single-Threaded-modification of the collection while enumerating erlauben, aber es gibt ein performance hit, die mit Thread-Sicherheit kommt, manchmal ein ziemlich großen.

Also die logische Sache für einen Framework-Entwickler zu tun ist, halten Sie die Klasse so leistungsfähig wie möglich für die 95% der Benutzer, die wahrscheinlich Threading nicht tun und verlassen sich auf diejenigen, die Multithread zu wissen, was sie haben zu tun, um es sicher zu halten.

0

Verwendung SynchronizedCollection es bietet auch einen Konstruktor-Parameter eine gemeinsame Sync :)