Ich verstehe, dass eine Liste im Allgemeinen nicht threadsicher ist. Ist es jedoch falsch, einfach Elemente zu einer Liste hinzuzufügen, wenn die Threads niemals andere Vorgänge in der Liste ausführen (z. B. das Durchlaufen)?
Beispiel:
List<object> list = new List<object>();
Parallel.ForEach(transactions, tran =>
{
list.Add(new object());
});
Antworten:
Hinter den Kulissen passieren viele Dinge, einschließlich der Neuzuweisung von Puffern und des Kopierens von Elementen. Dieser Code wird eine Gefahr verursachen. Ganz einfach, es gibt keine atomaren Operationen beim Hinzufügen zu einer Liste, zumindest muss die Eigenschaft "Länge" aktualisiert werden, und das Element muss an der richtigen Stelle eingefügt werden, und (wenn es eine separate Variable gibt) der Index muss aktualisiert werden. Mehrere Threads können übereinander treten. Und wenn ein Wachstum erforderlich ist, ist noch viel mehr los. Wenn etwas in eine Liste schreibt, sollte nichts anderes darin lesen oder schreiben.
In .NET 4.0 gibt es gleichzeitige Sammlungen, die handlich threadsicher sind und keine Sperren erfordern.
quelle
ConcurrentList
Typ gibt. Es gibt gleichzeitig Taschen, Wörterbücher, Stapel, Warteschlangen usw., aber keine Listen.Ihr aktueller Ansatz ist nicht threadsicher - ich würde vorschlagen, dies insgesamt zu vermeiden -, da Sie im Grunde genommen eine Datentransformation durchführen. PLINQ könnte ein besserer Ansatz sein (ich weiß, dass dies ein vereinfachtes Beispiel ist, aber am Ende projizieren Sie jede Transaktion in einen anderen "Zustand" " Objekt).
quelle
Parallel.Foreach
Überladung verwenden, die den Index berücksichtigt. In diesem Fall manipuliert jeder Thread einen anderen Array-Eintrag, und Sie sollten sicher sein.Es ist nicht unvernünftig zu fragen. Es gibt Fälle, in denen Methoden, die in Kombination mit anderen Methoden Probleme mit der Thread-Sicherheit verursachen können, sicher sind, wenn sie die einzige aufgerufene Methode sind.
Dies ist jedoch eindeutig kein Fall, wenn Sie den im Reflektor angezeigten Code betrachten:
Selbst wenn
EnsureCapacity
es an sich threadsicher war (und dies ist es mit Sicherheit nicht), wird der obige Code eindeutig nicht threadsicher sein, wenn man bedenkt, dass gleichzeitige Aufrufe des Inkrementoperators zu Fehlschreibvorgängen führen können.Entweder sperren, ConcurrentList verwenden oder eine Warteschlange ohne Sperre als Ort verwenden, an den mehrere Threads schreiben, und das Lesen daraus - entweder direkt oder durch Füllen einer Liste -, nachdem sie ihre Arbeit erledigt haben (ich gehe davon aus) Mehrere gleichzeitige Schreibvorgänge, gefolgt von Lesen mit einem Thread, sind hier Ihr Muster, gemessen an Ihrer Frage, da ich sonst nicht sehen kann, wie die Bedingung, bei der
Add
die einzige aufgerufene Methode aufgerufen wird, von Nutzen sein könnte.quelle
Wenn Sie
List.add
aus mehreren Threads verwenden möchten und sich nicht um die Reihenfolge kümmern möchten, benötigen Sie wahrscheinlichList
ohnehin nicht die Indizierungsfunktion von a und sollten stattdessen einige der verfügbaren gleichzeitigen Sammlungen verwenden.Wenn Sie diesen Rat ignorieren und nur tun
add
, können Sie denadd
Thread sicher, aber in unvorhersehbarer Reihenfolge wie folgt machen:Wenn Sie diese unvorhersehbare Bestellung akzeptieren, benötigen Sie wahrscheinlich, wie bereits erwähnt, keine Sammlung, die wie in indiziert indiziert werden kann
someList[i]
.quelle
Dies würde Probleme verursachen, da die Liste über ein Array erstellt wird und nicht threadsicher ist. Je nachdem, wo sich die Threads befinden, kann es vorkommen, dass der Index außerhalb der Grenzen liegt oder einige Werte andere Werte überschreiben. Mach es im Grunde nicht.
Es gibt mehrere mögliche Probleme ... Nur nicht. Wenn Sie eine thread-sichere Sammlung benötigen, verwenden Sie entweder eine Sperre oder eine der System.Collections.Concurrent-Sammlungen.
quelle
Ich habe mein Problem gelöst,
ConcurrentBag<T>
anstattList<T>
wie folgt:quelle
Kurze Antwort: ja.
Lange Antwort: Führen Sie das folgende Programm aus.
quelle
Wie bereits erwähnt, können Sie gleichzeitige Sammlungen aus dem
System.Collections.Concurrent
Namespace verwenden. Wenn Sie eine davon verwenden können, wird dies bevorzugt.Wenn Sie jedoch wirklich eine Liste wünschen, die nur synchronisiert ist, können Sie sich die
SynchronizedCollection<T>
Klasse in ansehenSystem.Collections.Generic
.Beachten Sie, dass Sie die System.ServiceModel-Assembly einschließen mussten, was auch der Grund ist, warum es mir nicht so gut gefällt. Aber manchmal benutze ich es.
quelle
Selbst das Hinzufügen von Elementen zu verschiedenen Threads ist nicht threadsicher.
In C # 4.0 gibt es gleichzeitige Sammlungen (siehe http://jiezhu0815.blogspot.com/2010/08/c-40-feature-1-concurrent-collections.html ).
quelle