Zum folgenden Tutorial: Ссылка
Sie sagen, dass der folgende Code:
%Vor%ist nicht deterministisch und kann folgende Antwort liefern:
0223557799
Ich dachte, dass wenn man Lambda-Ausdrücke verwendet, der Compiler eine Art anonymer Klasse erstellt, die die verwendeten Variablen erfasst, indem man solche Member in der Capturing-Klasse erstellt.
Aber i
ist Werttyp, also dachte ich, dass er nach Wert kopiert werden sollte.
wo ist mein Fehler?
Es wird sehr hilfreich sein, wenn die Antwort erklären wird, wie die Schließung funktioniert, wie sie einen "Zeiger" auf ein bestimmtes int hält, welcher Code in diesem speziellen Fall generiert wird?
Der entscheidende Punkt hier ist, dass Closures über Variablen schließen, nicht über Werte . Daher ist der Wert einer gegebenen Variablen zum Zeitpunkt des Schließens irrelevant. Was zählt, ist der Wert dieser Variablen zu dem Zeitpunkt, an dem die anonyme Methode aufgerufen wird .
Wie dies geschieht, ist leicht genug zu sehen, wenn Sie sehen, in was der Compiler die Schließung verwandelt. Es wird etwas moralisch ähnliches ergeben:
%Vor% Hier können wir also ein bisschen klarer sehen, was vor sich geht. Es gibt eine Kopie der Variablen, und diese Variable wurde jetzt zu einem Feld einer neuen Klasse hochgestuft, anstatt eine lokale Variable zu sein. Überall, wo die lokale Variable geändert würde, wird das Feld dieser Instanz geändert. Wir können jetzt sehen, warum Ihr Code das druckt, was er tut. Nach dem Start des neuen Threads, aber bevor dieser tatsächlich ausgeführt werden kann, geht die Schleife for
im Haupt-Thread zurück und inkrementiert die Variable im Abschluss. Die Variable, die noch nicht vom Abschluss gelesen wurde.
Um das gewünschte Ergebnis zu erzielen, müssen Sie sicherstellen, dass jede Schleife, die sich über eine einzelne Variable schließt, nicht eine Variable enthalten muss, die sie schließen:
%Vor% Nun wird die Variable copy
niemals geändert, nachdem sie geschlossen wurde, und unser Programm wird 0-9 ausgeben (obwohl in einer beliebigen Reihenfolge, weil die Threads geplant werden können, wie es das Betriebssystem will).
As Albahari
, Obwohl die übergebenen Argumente Werttypen sind, erfasst jeder Thread memory location
, was zu unerwarteten Ergebnissen führt.
Dies passiert, weil der Loop bereits vor dem Start des Threads den Wert von i
geändert hat.
Um das zu vermeiden, sollten Sie eine temp
Variable als Albahari
angegeben verwenden oder sie nur verwenden, wenn Sie wissen, dass sich die Variable nicht ändern wird.
i
in Console.Write(i)
wird direkt ausgewertet, wenn diese Anweisung ausgeführt wird. Diese Anweisung wird ausgeführt, sobald der Thread vollständig erstellt wurde und gestartet wird und zu diesem Code gelangt. Zu diesem Zeitpunkt hat sich die Schleife ein paar Mal vorwärts bewegt und somit kann i
bis dahin ein beliebiger Wert sein. Verschlüsse haben im Gegensatz zu normalen Funktionen Einblick in lokale Variablen einer Funktion, in der sie definiert sind (was sie nützlich macht und wie man sich in einen Fuß schießt).