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!
Faden A:
%Vor%Faden B:
%Vor%Das ist natürlich schlecht für die Leistung.
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.
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.
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.
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.
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.
Tags und Links c# multithreading thread-safety list