Ich möchte eine Implementierung List<T>
als Eigenschaft, die ohne Zweifel threadsicher verwendet werden kann.
Etwas wie das:
private List<T> _list;
private List<T> MyT
{
get { // return a copy of _list; }
set { _list = value; }
}
Es scheint immer noch, dass ich eine Kopie (geklont) der Sammlung zurückgeben muss. Wenn wir also irgendwo die Sammlung iterieren und gleichzeitig die Sammlung festgelegt wird, wird keine Ausnahme ausgelöst.
Wie implementiere ich eine thread-sichere Sammlungseigenschaft?
c#
collections
properties
thread-safety
Xaqron
quelle
quelle
IList<T>
(vsList<T>
) verwenden?List<T>
implementiert wird? Wenn ja, können Sie bitte eine Schnittstelle bereitstellen, die Sie benötigen, anstatt nach allem zu fragen, wasList<T>
bereits vorhanden ist?Antworten:
Wenn Sie auf .NET 4 abzielen, gibt es in System.Collections.Concurrent Namespace einige Optionen
Sie könnten
ConcurrentBag<T>
in diesem Fall anstelle von verwendenList<T>
quelle
ConcurrentBag
ist eine ungeordnete Abholung, so dass im GegensatzList<T>
dazu keine Garantie für die Bestellung besteht. Sie können auch nicht über den Index auf Elemente zugreifen.Selbst wenn es die meisten Stimmen bekommen hat, kann man es normalerweise nicht
System.Collections.Concurrent.ConcurrentBag<T>
alsSystem.Collections.Generic.List<T>
fadensicheren Ersatz nehmen, da es (Radek Stromský hat bereits darauf hingewiesen) nicht bestellt wurde.Aber es gibt eine Klasse namens
System.Collections.Generic.SynchronizedCollection<T>
, die bereits seit .NET 3.0 Teil des Frameworks ist, aber sie ist so gut versteckt an einem Ort, an dem man nicht erwartet, dass sie wenig bekannt ist und Sie wahrscheinlich noch nie darüber gestolpert sind (zumindest nicht) Ich habe nie getan).SynchronizedCollection<T>
wird in die Assembly System.ServiceModel.dll kompiliert (die Teil des Clientprofils, aber nicht der portablen Klassenbibliothek ist).Hoffentlich hilft das.
quelle
Ich würde denken, dass es einfach wäre, eine Beispiel-ThreadSafeList-Klasse zu erstellen:
Sie klonen einfach die Liste, bevor Sie einen Enumerator anfordern. Daher funktioniert jede Aufzählung mit einer Kopie, die während der Ausführung nicht geändert werden kann.
quelle
T
es sich um einen Referenztyp handelt, wird dann nicht einfach eine neue Liste mit Verweisen auf alle Originalobjekte zurückgegeben? In diesem Fall kann dieser Ansatz immer noch zu Threading-Problemen führen, da mehrere Threads über verschiedene "Kopien" der Liste auf die Listenobjekte zugreifen können.newList
wurden keine Elemente hinzugefügt oder entfernt, die den Enumerator ungültig machen würden).Selbst die akzeptierte Antwort ist ConcurrentBag. Ich denke nicht, dass dies in allen Fällen eine echte Ersetzung der Liste darstellt, da Radeks Kommentar zur Antwort lautet: "ConcurrentBag ist eine ungeordnete Sammlung, daher garantiert sie im Gegensatz zu List keine Bestellung. Außerdem können Sie nicht auf Artikel nach Index zugreifen." ".
Wenn Sie also .NET 4.0 oder höher verwenden, besteht eine Problemumgehung darin, ConcurrentDictionary mit der Ganzzahl TKey als Array-Index und TValue als Array-Wert zu verwenden. Dies wird empfohlen, um die Liste im C # Concurrent Collections-Kurs von Pluralsight zu ersetzen . ConcurrentDictionary löst beide oben genannten Probleme: Indexzugriff und -reihenfolge (wir können uns nicht auf die Reihenfolge verlassen, da es sich um eine Hash-Tabelle unter der Haube handelt, aber die aktuelle .NET-Implementierung spart die Reihenfolge des Hinzufügens von Elementen).
quelle
ConcurrentDictionary
ist ein Wörterbuch, keine Liste. 2) Es wird nicht garantiert, dass die Reihenfolge beibehalten wird, wie in Ihrer eigenen Antwort angegeben, was Ihrem angegebenen Grund für die Veröffentlichung einer Antwort widerspricht. 3) Es wird auf ein Video verlinkt, ohne die entsprechenden Zitate in diese Antwort aufzunehmen (was möglicherweise ohnehin nicht mit ihrer Lizenzierung übereinstimmt).current implementation
der Dokumentation nicht ausdrücklich garantiert sind. Die Implementierung kann jederzeit ohne vorherige Ankündigung geändert werden.Die
ArrayList
Klasse von C # hat eineSynchronized
Methode.Dies gibt einen thread-sicheren Wrapper um jede Instanz von zurück
IList
. Alle Vorgänge müssen über den Wrapper ausgeführt werden, um die Gewindesicherheit zu gewährleisten.quelle
Wenn Sie sich den Quellcode für List of T ansehen ( https://referencesource.microsoft.com/#mscorlib/system/collections/generic/list.cs,c66df6f36c131877 ), werden Sie feststellen, dass dort eine Klasse vorhanden ist (was natürlich der Fall ist) intern - warum, Microsoft, warum?!?!) namens SynchronizedList of T. Ich kopiere den Code hier und füge ihn ein:
Persönlich denke ich, dass sie wussten, dass eine bessere Implementierung mit SemaphoreSlim erstellt werden kann, aber nicht dazu gekommen sind.
quelle
_root
) bei jedem Zugriff (Lesen / Schreiben) macht dies zu einer langsamen Lösung. Vielleicht ist es besser für diese Klasse, intern zu bleiben.Clear()
nach dem anderen,this[index]
aber bevor die Sperre aktiviert wird.index
ist nicht mehr sicher zu verwenden und löst eine Ausnahme aus, wenn es endgültig ausgeführt wird.Sie können auch das primitivere verwenden
welche Sperre verwendet wird (siehe diesen Beitrag C # Sperren eines Objekts, das im Sperrblock neu zugewiesen wurde ).
Wenn Sie Ausnahmen im Code erwarten, ist dies nicht sicher, aber Sie können Folgendes tun:
Eines der schönen Dinge dabei ist, dass Sie die Sperre für die Dauer der Reihe von Operationen erhalten (anstatt jede Operation zu sperren). Das bedeutet, dass die Ausgabe in den richtigen Blöcken erfolgen sollte (meine Verwendung bestand darin, dass ein externer Prozess eine Ausgabe auf den Bildschirm brachte).
Ich mag die Einfachheit + Transparenz der ThreadSafeList +, die den wichtigen Beitrag zum Stoppen von Abstürzen leistet
quelle
In .NET Core (jede Version) können Sie ImmutableList verwenden , das über alle Funktionen von verfügt
List<T>
.quelle
Ich glaube
_list.ToList()
, Sie werden eine Kopie erhalten. Sie können es auch abfragen, wenn Sie Folgendes benötigen:Wie auch immer, msdn sagt, dies sei in der Tat eine Kopie und nicht nur eine Referenz. Oh, und ja, Sie müssen die festgelegte Methode sperren, wie die anderen betont haben.
quelle
Es scheint, dass viele der Leute, die dies finden, eine thread-sichere indizierte Sammlung mit dynamischer Größe wünschen. Das Nächste und Einfachste, von dem ich weiß, wäre.
Dies würde erfordern, dass Sie sicherstellen, dass Ihr Schlüssel ordnungsgemäß belastet wird, wenn Sie ein normales Indizierungsverhalten wünschen. Wenn Sie vorsichtig sind, kann .count als Schlüssel für alle neuen Schlüsselwertpaare ausreichen, die Sie hinzufügen.
quelle
Ich würde jedem empfehlen, der sich mit
List<T>
Multithreading-Szenarien befasst, sich unveränderliche Sammlungen anzuschauen, insbesondere das ImmutableArray .Ich fand es sehr nützlich, wenn Sie haben:
Kann auch nützlich sein, wenn Sie ein transaktionsähnliches Verhalten implementieren müssen (dh einen Einfüge- / Aktualisierungs- / Löschvorgang im Fehlerfall rückgängig machen).
quelle
Hier ist die Klasse, nach der Sie gefragt haben:
quelle
this.GetEnumerator();
wenn @Tejs vorschlägtthis.Clone().GetEnumerator();
?[DataContract( IsReference = true )]
?Hier ist die Klasse für die thread-sichere Liste ohne Sperre
quelle
Grundsätzlich müssen Sie lock verwenden, wenn Sie sicher aufzählen möchten.
Bitte beziehen Sie sich hierzu auf MSDN. http://msdn.microsoft.com/en-us/library/6sh2ey19.aspx
Hier ist ein Teil von MSDN, an dem Sie interessiert sein könnten:
Öffentliche statische (in Visual Basic freigegebene) Mitglieder dieses Typs sind threadsicher. Es wird nicht garantiert, dass Instanzmitglieder threadsicher sind.
Eine Liste kann mehrere Leser gleichzeitig unterstützen, solange die Sammlung nicht geändert wird. Das Aufzählen durch eine Sammlung ist an sich keine threadsichere Prozedur. In dem seltenen Fall, dass eine Aufzählung mit einem oder mehreren Schreibzugriffen konkurriert, besteht die einzige Möglichkeit, die Thread-Sicherheit zu gewährleisten, darin, die Sammlung während der gesamten Aufzählung zu sperren. Damit mehrere Threads zum Lesen und Schreiben auf die Sammlung zugreifen können, müssen Sie Ihre eigene Synchronisierung implementieren.
quelle
Verwenden Sie dazu die
lock
Anweisung. ( Lesen Sie hier für weitere Informationen. )Zu Ihrer Information, dies ist wahrscheinlich nicht genau das, was Sie gefragt haben - Sie möchten wahrscheinlich Ihren Code weiter ausschließen, aber das kann ich nicht annehmen. Schauen Sie sich das
lock
Schlüsselwort an und passen Sie es an Ihre spezifische Situation an.Wenn nötig, können Sie
lock
sowohl imget
als auch imset
Block die_list
Variable verwenden, wodurch das Lesen / Schreiben nicht gleichzeitig erfolgen kann.quelle
lock
Aussage schauen sollte . Anhand eines ähnlichen Beispiels könnte ich argumentieren, dass wir keine for-Schleifen verwenden sollten, da Sie die Anwendung mit kaum Aufwandfor (int x = 0; x >=0; x += 0) { /* Infinite loop, oops! */ }
lock (this)
undlock (typeof(this))
sind große No-No's.