Ich vermute, dass der Optimierer durch das Fehlen eines 'flüchtigen' Schlüsselworts für die isCompleteVariable getäuscht wird .
Natürlich können Sie es nicht hinzufügen, da es sich um eine lokale Variable handelt. Und da es sich um eine lokale Variable handelt, sollte sie natürlich überhaupt nicht benötigt werden, da die Einheimischen auf dem Stapel bleiben und natürlich immer "frisch" sind.
Jedoch , nach dem Kompilieren, es ist nicht mehr eine lokale Variable . Da in einem anonymen Delegaten darauf zugegriffen wird, wird der Code aufgeteilt und in eine Hilfsklasse und ein Mitgliedsfeld übersetzt.
public static void Main(string[] args)
{
TheHelper hlp = new TheHelper();
var t = new Thread(hlp.Body);
t.Start();
Thread.Sleep(500);
hlp.isComplete = true;
t.Join();
Console.WriteLine("complete!");
}
private class TheHelper
{
public bool isComplete = false;
public void Body()
{
int i = 0;
while (!isComplete) i += 0;
}
}
Ich kann jetzt vorstellen , dass der JIT - Compiler / Optimierer in einer Multithread - Umgebung, bei der Verarbeitung von TheHelperKlasse, kann tatsächlich zwischenspeichern Sie den Wert falsein einem gewissen Register oder Stapelrahmen zu Beginn des Body()Verfahrens, und es nie , bis das Verfahren endet aktualisieren. Das liegt daran, dass es KEINE GARANTIE gibt, dass der Thread und die Methode NICHT enden, bevor "= true" ausgeführt wird. Wenn es also keine Garantie gibt, warum nicht zwischenspeichern und die Leistung steigern, indem das Heap-Objekt einmal gelesen wird, anstatt es bei jedem zu lesen Wiederholung.
Genau aus diesem Grund volatileexistiert das Schlüsselwort .
Für diese Helfer-Klasse sein richtig ein kleines bisschen besser 1) in Multi-Threaded - Umgebungen, sollte es haben:
public volatile bool isComplete = false;
Da es sich jedoch um automatisch generierten Code handelt, können Sie ihn natürlich nicht hinzufügen. Ein besserer Ansatz wäre, einige lock()s um Lese- und Schreibvorgänge hinzuzufügen isCompletedoder andere gebrauchsfertige Synchronisierungs- oder Threading- / Tasking-Dienstprogramme zu verwenden, anstatt zu versuchen, dies Bare-Metal zu tun (was nicht Bare-Metal sein wird). da es C # auf CLR mit GC, JIT und (..) ist.
Der Unterschied im Debug-Modus tritt wahrscheinlich auf, weil im Debug-Modus viele Optimierungen ausgeschlossen sind, sodass Sie den auf dem Bildschirm angezeigten Code debuggen können. Daher while (!isComplete)ist es nicht optimiert, sodass Sie dort einen Haltepunkt setzen können, und wird daher isCompletebeim Methodenstart nicht aggressiv in einem Register oder Stapel zwischengespeichert und bei jeder Schleifeniteration aus dem Objekt auf dem Heap gelesen.
Übrigens. Das sind nur meine Vermutungen. Ich habe nicht einmal versucht, es zu kompilieren.
Übrigens. Es scheint kein Fehler zu sein; Es ist eher eine sehr dunkle Nebenwirkung. Wenn ich damit recht habe, kann es sich auch um einen Sprachmangel handeln. Mit C # sollte es möglich sein, lokale Variablen, die erfasst und in Mitgliedsfelder in den Abschlüssen befördert werden, mit dem Schlüsselwort "flüchtig" zu versehen.
1) Siehe unten für einen Kommentar von Eric Lippert zu volatileund / oder diesen sehr interessanten Artikel, der zeigt, wie komplex es ist, sicherzustellen, dass Code, auf den volatileman sich verlässt, sicher ist. Gut, gut. Sagen wir OK.