2010-05-12 10 views
12

Ich plane, die Liste einmal in einem statischen Konstruktor zu erstellen und dann mehrere Instanzen dieser Klasse gleichzeitig zu lesen (und aufzuzählen), ohne eine Sperre durchzuführen.Threadsicherheit der C# -Liste <T> für Leser

In diesem Artikel http://msdn.microsoft.com/en-us/library/6sh2ey19.aspx MS beschreibt die Probleme, die Sicherheit der Faden wie folgt:

öffentlichen statischen (Shared in Visual Basic) Member dieses Typs sind threadsicher. Alle Instanzmitglieder sind nicht garantiert Thread sicher sein.

Eine Liste kann mehrere Leser gleichzeitig unterstützen, solange die Sammlung nicht geändert wird. Aufzählung durch eine Sammlung ist intrinsisch kein Thread-sicheres Verfahren. In dem seltenen Fall, in dem eine Enumeration mit einem oder mehreren Schreibzugriffen konkurriert, ist die einzige Möglichkeit, Thread-Sicherheit sicherzustellen, die Sammlung während der gesamten Enumeration zu sperren. Damit die Sammlung auf mehrere Threads für Lesen und Schreiben zugreifen kann, müssen Sie Ihre eigene Synchronisierung implementieren.

Die "Aufzählung durch eine Sammlung ist an sich kein Thread-sicheres Verfahren." Statement macht mir Sorgen.

Bedeutet dies, dass es threadsicher für nur Leser-Szenario ist, aber solange Sie keine Aufzählung verwenden?

Oder ist es sicher für mein Szenario?


Danke für die Antworten. Warum muss ich AsReadOnly überhaupt verwenden, wenn es mit oder ohne es funktioniert?

Antwort

1

Bedeutet dies, dass es Thread-sicher für die Leser nur Szenario ist, aber so lange, wie Sie nicht Aufzählung verwenden Sie? Oder ist es sicher für mein Szenario?

Hängt davon ab, wann Sie in die Sammlung schreiben. Das kann nicht mit dem Lesen (Aufzählen) ohne irgendeine Art von Sperrschema zusammenfallen.

Also wenn Sie es einmal ausfüllen und dann nur iterieren, sind Sie sicher. Wenn jedoch ein Thread die Liste ändert (Elemente hinzufügen oder entfernen), benötigen Sie beispielsweise einen ReaderWriterLockSlim.

Wenn Sie den Status eines gespeicherten Elements ändern, gilt die Thread-Sicherheit für dieses Element (nicht für die Liste).

8

Sie meinen, wenn Sie eine Sammlung aufzählen, während ein anderer Thread (oder Ihr eigener Thread) es ändert, haben Sie Probleme.

Solange Sie die Sammlung überhaupt nicht ändern und solange Sie keine IEnumerator s über Threads teilen, sollten Sie keine Probleme haben.

+0

Mit 'IEnumerators nicht über Threads teilen 'meinst du' nicht Zugriff ** auf die gleiche Enumerator-Instanz ** von mehreren Threads'? –

+1

@EugeneBeresovksy: Genau. – SLaks

3

Ja, die Liste ist sicher für eine Leser nur Szenario, wenn die Liste wird nie geändert werden dann wird es in Ordnung sein.

Wenn es in der Tat der Fall ist, dass nach der Konstruktion die Liste nicht geändert wird, dann sollten Sie eine geeignetere Schnittstelle wie ReadOnlyCollection verwenden. wenn es in einer öffentlichen statischen Variable gespeichert ist, wie Sie sagen, sollten Sie diese Schnittstelle verwenden.

private static List<T> shared_list; 

private static ReadOnlyCollection<T> _data; 
public static IEnumerable<T> Data 
{ 
    get 
    { 
     return _data ?? (_data = shared_list.AsReadOnly()); 
    } 
} 

==== ==== EDIT

Diese Version speichert die Readonlycollection Referenz für eine schnellere Zukunft Lookup-mal.

Hinweis, es gibt eine Möglichkeit für ein Datenrennen auf der Variable _data auftreten, wenn zwei Threads versuchen, eine Referenz gleichzeitig zu greifen, wenn es Null ist, aber weil alles gelesen wird, ist dies nicht wirklich wichtig, werden wir nur Erstellen Sie ein zusätzliches ReadOnlyCollection-Objekt, das im Vergleich zur Synchronisierung billig ist.

+0

Beachten Sie, dass Sie die ursprüngliche Liste immer noch nicht ändern können. Rufen Sie 'AsReadOnly' nicht jedes Mal auf, wenn Sie die Eigenschaft erhalten. – SLaks

+0

AsReadOnly ist eine O (1) -Operation entsprechend der MSDN-Dokumentation. Ich glaube, es wird einfach mit einer ReadOnlyCollection umgebrochen, die einfach an die zugrunde liegende Sammlung delegiert. AsReadOnly sollte also ziemlich schnell sein, aber es verursacht eine Zuweisung, so dass wir diesen Speicher möglicherweise gemeinsam nutzen können. schlecht bearbeiten, um zu reflektieren. – luke

+0

Unter .NET 4 können Sie die generische Lazy-Klasse für die Initialisierung verwenden. Siehe http://msdn.microsoft.com/en-us/library/dd642329.aspx – TrueWill