Ändern der Liste von einem anderen Thread beim Iterieren (C #)

7

Ich blättere durch eine Liste von Elementen mit foreach, wie folgt:

%Vor%

Allerdings rufe ich in einem anderen Thread etwas wie

an %Vor%

Dies verursacht während der Laufzeit eine InvalidOperationException: Die Sammlung wurde geändert. Aufzählungsoperation wird möglicherweise nicht ausgeführt. Was ist der beste Weg, damit umzugehen (ich würde es sogar auf Kosten der Leistung als ziemlich einfach bezeichnen)?

Danke!

    
Henrik Karlsson 04.04.2012, 19:17
quelle

6 Antworten

7

Faden A:

%Vor%

Faden B:

%Vor%

Das ist natürlich schlecht für die Leistung.

    
Eugen Rieck 04.04.2012, 19:19
quelle
9
  

Was ist der beste Weg, damit umzugehen (ich würde es sogar auf Kosten der Leistung als ziemlich einfach bezeichnen)?

Grundsätzlich: Versuchen Sie nicht, eine nicht threadsichere Sammlung aus mehreren Threads ohne Sperren zu ändern. Die Tatsache, dass du iterierst, ist hier meistens irrelevant - es hat dir nur geholfen, es schneller zu finden. Es wäre für zwei Threads unsicher gewesen, beide Remove gleichzeitig aufzurufen.

Entweder verwenden eine threadsichere Sammlung wie ConcurrentBag oder stellen Sie sicher, dass nur ein Thread irgendetwas gleichzeitig mit der Sammlung ausführt.

    
Jon Skeet 04.04.2012 19:19
quelle
9

Methode # 1:

Die einfachste und am wenigsten effiziente Methode besteht darin, einen kritischen Abschnitt für die Leser und Schreiber zu erstellen.

%Vor%

Methode 2:

Dies ist ähnlich wie Methode # 1, aber anstatt die Sperre für die gesamte Dauer der foreach -Schleife zu halten, würden Sie zuerst die Sammlung kopieren und dann über die Kopie iterieren.

%Vor%

Methode # 3:

Es hängt alles von Ihrer spezifischen Situation ab, aber die Art und Weise, wie ich normalerweise damit umgehe, ist, den Master-Bezug auf die Sammlung unveränderlich zu halten. Auf diese Weise müssen Sie den Zugriff auf Leserseite nie synchronisieren. Die Verfasserseite der Dinge benötigt ein lock . Die Leserseite braucht nichts, was bedeutet, dass die Leser hochgradig parallel bleiben. Das einzige, was Sie tun müssen, ist die aList -Referenz als volatile zu markieren.

%Vor%     
Brian Gideon 04.04.2012 20:01
quelle
1

Wenn Sie mehr als einen Reader haben, versuchen Sie eine Reader-Writer-Sperre (.Net 3.5+), Slim: Ссылка

Wenn Sie nur einen Leser haben, der nur eine Sperre auf der Liste selbst oder ein privates Objekt (aber nicht auf den Typ selbst sperren), wie in Eugen Riecks Antwort gezeigt.

    
user1198042 04.04.2012 19:36
quelle
0

Wenn Sie nur die Verwendung von Ausnahmen vermeiden möchten

%Vor%

beachten Sie die     etwas tun() wird auch ausgeführt, wenn das Element im anderen Thread entfernt wurde

    
NoMe 04.04.2012 19:31
quelle
0

Ich kann nicht spezifisch von Ihrer Frage erzählen, aber (es sieht so aus) Sie führen eine Aktion für jeden Gegenstand durch und entfernen sie dann. Vielleicht möchten Sie in BlockingCollection<T> nachschauen, wo eine Methode aufgerufen wird, die GetConsumingEnumerable() aufruft, um zu sehen, ob sie für Sie geeignet ist. Hier ist ein kleines Beispiel.

%Vor%     
Bryan Crosby 04.04.2012 19:33
quelle