Je suppose que l'optimiseur est trompé par l'absence du mot-clé " volatile " dans le fichier de données. isComplete
variable.
Bien sûr, vous ne pouvez pas l'ajouter, car il s'agit d'une variable locale. Et bien sûr, puisque c'est une variable locale, on ne devrait pas en avoir besoin du tout, parce que les variables locales sont conservées sur pile et ils sont naturellement toujours "frais".
Cependant après la compilation, c'est n'est plus une variable locale . Comme on y accède dans un délégué anonyme, le code est divisé, et il est traduit en une classe d'aide et un champ membre, quelque chose comme :
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;
}
}
Je peux imaginer maintenant que le compilateur/optimiseur JIT dans un environnement multithread, lorsqu'il traite TheHelper
peut en fait cache la valeur false
dans un registre ou une trame de pile au début de l'opération. Body()
et ne le rafraîchissez jamais avant la fin de la méthode. C'est parce qu'il n'y a AUCUNE GARANTIE que le thread&méthode ne se terminera PAS avant que le "=true" ne soit exécuté, donc s'il n'y a aucune garantie, alors pourquoi ne pas le mettre en cache et bénéficier de l'augmentation des performances en lisant l'objet tas une fois au lieu de le lire à chaque itération.
C'est exactement pourquoi le mot-clé volatile
existe.
Pour que cette classe d'aide soit correct un tout petit peu mieux 1) dans les environnements multithreads, il devrait l'avoir fait :
public volatile bool isComplete = false;
mais, bien sûr, comme il s'agit d'un code autogénéré, vous ne pouvez pas l'ajouter. Une meilleure approche serait d'ajouter des lock()
autour des lectures et des écritures dans isCompleted
ou d'utiliser d'autres utilitaires de synchronisation ou de threading/tasking prêts à l'emploi au lieu d'essayer de le faire à nu (ce qui n'est pas le cas). ne sera pas bare-metal, puisque c'est C# sur CLR avec GC, JIT et ( )).
La différence en mode de débogage s'explique probablement par le fait qu'en mode de débogage, de nombreuses optimisations sont exclues, de sorte que vous pouvez déboguer le code que vous voyez à l'écran. Par conséquent, while (!isComplete)
n'est pas optimisé pour que vous puissiez y placer un point d'arrêt, et donc isComplete
n'est pas mis en cache de manière agressive dans un registre ou une pile au début de la méthode et est lu depuis l'objet sur le tas à chaque itération de la boucle.
BTW. C'est juste mes suppositions sur ça. Je n'ai même pas essayé de le compiler.
BTW. Il ne semble pas s'agir d'un bogue, mais plutôt d'un effet secondaire très obscur. De plus, si j'ai raison, il peut s'agir d'une déficience du langage - C# devrait permettre de placer le mot clé 'volatile' sur les variables locales qui sont capturées et promues en champs membres dans les closures.
1) voir ci-dessous pour un commentaire d'Eric Lippert sur volatile
et/ou cet article très intéressant montrant les niveaux de complexité impliqués pour assurer que le code s'appuyant sur volatile
es sûr ..uh, bon ..euh, disons OK.