Basierend auf meinem Verständnis ein C # Array gegeben, der Akt der gleichzeitig von mehreren Threads über das Array Iterieren ist ein Thread einen sicheren Betrieb.
Mit Iteration über das Array meine ich das Lesen aller Positionen innerhalb des Arrays mittels einer einfachen alten for
Schleife . Jeder Thread liest einfach den Inhalt eines Speicherplatzes innerhalb des Arrays. Niemand schreibt etwas, sodass alle Threads auf konsistente Weise dasselbe lesen.
Dies ist ein Stück Code, der das tut, was ich oben geschrieben habe:
public class UselessService
{
private static readonly string[] Names = new [] { "bob", "alice" };
public List<int> DoSomethingUseless()
{
var temp = new List<int>();
for (int i = 0; i < Names.Length; i++)
{
temp.Add(Names[i].Length * 2);
}
return temp;
}
}
Mein Verständnis ist also, dass die Methode DoSomethingUseless
threadsicher ist und dass es nicht erforderlich ist , die string[]
durch einen threadsicheren Typ zu ersetzen (wie ImmutableArray<string>
zum Beispiel).
Hab ich recht ?
Nehmen wir nun an, wir haben eine Instanz von IEnumerable<T>
. Wir wissen nicht, was das zugrunde liegende Objekt ist, wir wissen nur, dass ein Objekt implementiert ist IEnumerable<T>
, sodass wir es mithilfe der foreach
Schleife durchlaufen können .
Nach meinem Verständnis gibt es in diesem Szenario keine Garantie dafür, dass das gleichzeitige Durchlaufen dieses Objekts von mehreren Threads eine threadsichere Operation ist. Anders ausgedrückt, es ist durchaus möglich, dass das gleichzeitige Durchlaufen der IEnumerable<T>
Instanz von verschiedenen Threads den internen Status des Objekts unterbricht, sodass es beschädigt wird.
Bin ich in diesem Punkt richtig?
Was ist mit der IEnumerable<T>
Implementierung der Array
Klasse? Ist es threadsicher?
Anders ausgedrückt, ist der folgende Code-Thread sicher? (Dies ist genau der gleiche Code wie oben, aber jetzt wird das Array durch Verwendung einer foreach
Schleife anstelle einer for
Schleife iteriert. )
public class UselessService
{
private static readonly string[] Names = new [] { "bob", "alice" };
public List<int> DoSomethingUseless()
{
var temp = new List<int>();
foreach (var name in Names)
{
temp.Add(name.Length * 2);
}
return temp;
}
}
Gibt es eine Referenz, die angibt, welche IEnumerable<T>
Implementierungen in der .NET-Basisklassenbibliothek tatsächlich threadsicher sind?
quelle
Array
) ist threadsicher, solange alle Threads es nur lesen. GleichesList<T>
gilt für und wahrscheinlich für jede andere von Microsoft geschriebene Sammlung. Es ist jedoch möglich, eigene zu erstellenIEnumerable
, die nicht threadsichere Dinge im Enumerator ausführen. Daher ist es keine Garantie dafür, dass alles, was implementiertIEnumerable
wird, threadsicher ist.foreach
funktioniert mit mehr als nur Enumeratoren. Der Compiler ist intelligent genug, um zu wissen, dass er bei statischen Typinformationen, die darauf hinweisen, dass es sich bei der Auflistung um ein Array handelt, die Generierung des Enumerators überspringt und direkt auf das Array zugreift, als wäre es einefor
Schleife. Wenn die Sammlung eine benutzerdefinierte Implementierung von unterstütztGetEnumerator
, wird diese Implementierung ebenfalls verwendet, anstatt sie aufzurufenIEnumerable.GetEnumerator
. Die Vorstellung, dassforeach
nur Menschenhandel stattfindet,IEnumerable/IEnumerator
ist falsch.volatile
garantiert nicht, dass Sie die aktuellste Aktualisierung einer Variablen erhalten, da C # es verschiedenen Threads ermöglicht, unterschiedliche Lese- und Schreibreihenfolgen zu beobachten, und daher der Begriff "frischeste" nicht global definiert ist . Anstatt über Frische nachzudenken, sollten Sie darüber nachdenken, welche Einschränkungenvolatile
die Nachbestellung von Schreibvorgängen einschränken .Antworten:
Ist das Iterieren über ein Array mit einer for-Schleife eine thread-sichere Operation in C #?
Wenn Sie ausschließlich über das Lesen aus mehreren Threads sprechen , ist dies threadsicher
Array
undList<T>
nahezu jede von Microsoft geschriebene Sammlung , unabhängig davon, ob Sie einefor
oder eineforeach
Schleife verwenden. Besonders in dem Beispiel, das Sie haben:Sie können dies über so viele Threads tun, wie Sie möchten. Sie werden alle
Names
glücklich die gleichen Werte lesen .Wenn Sie aus einem anderen Thread darauf schreiben (dies war nicht Ihre Frage, aber es ist erwähnenswert)
Iterieren über eine
Array
oderList<T>
mit einerfor
Schleife , wird einfach weiter gelesen, und die geänderten Werte werden gerne gelesen, wenn Sie auf sie stoßen.Iterieren mit einem
foreach
Schleife , hängt dies von der Implementierung ab. Wenn sich ein Wert aufArray
halbem Weg durch eineforeach
Schleife ändert , werden nur die Aufzählungen fortgesetzt und Sie erhalten die geänderten Werte.Mit
List<T>
kommt es darauf an, was Sie als "threadsicher" betrachten. Wenn Sie sich mehr mit dem Lesen genauer Daten befassen, ist dies "sicher", da es eine Ausnahme in der Mitte der Aufzählung auslöst und Ihnen mitteilt, dass sich die Sammlung geändert hat. Wenn Sie jedoch eine Ausnahme als nicht sicher betrachten, ist sie nicht sicher.Es ist jedoch erwähnenswert, dass dies eine Designentscheidung in ist
List<T>
ist. Es gibt Code , der explizit nach Änderungen sucht und eine Ausnahme auslöst. Designentscheidungen bringen uns zum nächsten Punkt:Können wir davon ausgehen, dass jeder implementierte Sammlung
IEnumerable
sicher über mehrere Threads hinweg gelesen werden kann?In den meisten Fällen ist dies der Fall, aber das threadsichere Lesen ist nicht garantiert. Der Grund dafür ist, dass für jede
IEnumerable
Implementierung eine Implementierung erforderlich istIEnumerator
, die entscheidet, wie die Elemente in der Sammlung durchlaufen werden. Und genau wie in jeder Klasse können Sie alles tun, was Sie wollen , einschließlich nicht threadsicherer Dinge wie:Sie können sogar etwas Seltsames tun, z.
GetEnumerator()
B. jedes Mal, wenn der Enumerator aufgerufen wird, dieselbe Instanz zurückgeben. Das könnte wirklich zu unvorhersehbaren Ergebnissen führen.Ich halte etwas für nicht threadsicher, wenn es zu unvorhersehbaren Ergebnissen führen kann. Jedes dieser Dinge kann zu unvorhersehbaren Ergebnissen führen.
Sie können sehen , den Quellcode für die ,
Enumerator
dassList<T>
Anwendungen , so können Sie sehen , dass es nicht dieses seltsamen Ding tut, die Ihnen sagt , dass AufzählenList<T>
von mehreren Threads sicher ist.quelle
List<T>
oderArray
ist nur so sicher wieImmutableArray<string>
, da wir beweisen können, dass diese threadsicher zu lesen sind. Ich würde mich nur mit anderen Optionen befassen, wenn Sie vorhaben , über mehrere Threads hinweg zu lesen und zu schreiben . Sie könnten eine entsprechende Sammlung von verwendenSystem.Collections.Concurrent
.Um zu behaupten, dass Ihr Code threadsicher ist, müssen wir Ihre Worte als selbstverständlich betrachten, dass sich kein Code im Code befindet, der
UselessService
gleichzeitig versucht, den Inhalt desNames
Arrays durch so etwas zu ersetzen"tom" and "jerry"
oder (unheimlicher)null and null
. Auf der anderen SeiteImmutableArray<string>
würde die Verwendung eines garantieren dass der Code threadsicher ist, und jeder kann sich darauf verlassen, wenn er nur den Typ des statischen schreibgeschützten Felds betrachtet, ohne den Rest des Codes sorgfältig prüfen zu müssen.Sie können diese Kommentare aus dem interessant finden Quellcode von
ImmutableArray<T>
, was einige Implementierungsdetails dieser Struktur betrifft:quelle