Vous allez avoir beaucoup de mal à reproduire ce bug. En fait, j'irais même jusqu'à dire que vous ne pourrez jamais le reproduire en utilisant le .NET Framework. La raison en est que l'implémentation de Microsoft utilise un modèle de mémoire fort pour les écritures. Cela signifie que les écritures sont traitées comme si elles étaient volatiles. Une écriture volatile a une sémantique de verrouillage et de libération, ce qui signifie que toutes les écritures précédentes doivent être validées avant l'écriture actuelle.
Toutefois, la spécification de l'ECMA présente un modèle de mémoire plus faible. Il est donc théoriquement possible que Mono ou même une future version du .NET Framework commence à présenter ce comportement bogué.
Ce que je veux dire, c'est qu'il est très peu probable que la suppression des obstacles n° 1 et n° 2 ait un quelconque impact sur le comportement du programme. Bien sûr, ce n'est pas une garantie, mais une observation basée uniquement sur l'implémentation actuelle du CLR.
La suppression des obstacles n° 3 et n° 4 aura certainement un impact. C'est en fait assez facile à reproduire. Enfin, pas cet exemple en soi, mais le code suivant est l'une des démonstrations les plus connues. Il doit être compilé en utilisant la version Release et exécuté en dehors du débogueur. Le problème est que le programme ne se termine pas. Vous pouvez corriger ce bogue en plaçant un appel à Thread.MemoryBarrier
à l'intérieur de la while
ou en marquant stop
como volatile
.
class Program
{
static bool stop = false;
public static void Main(string[] args)
{
var t = new Thread(() =>
{
Console.WriteLine("thread begin");
bool toggle = false;
while (!stop)
{
toggle = !toggle;
}
Console.WriteLine("thread end");
});
t.Start();
Thread.Sleep(1000);
stop = true;
Console.WriteLine("stop = true");
Console.WriteLine("waiting...");
t.Join();
}
}
La raison pour laquelle certains bogues de threading sont difficiles à reproduire est que la même tactique que vous utilisez pour simuler l'entrelacement des fils peut en fait corriger le bogue. Thread.Sleep
est l'exemple le plus notable car il génère des barrières de mémoire. Vous pouvez le vérifier en plaçant un appel à l'intérieur de la fonction while
et observer que le bug disparaît.
Vous pouvez voir ma réponse aquí pour une autre analyse de l'exemple du livre que vous avez cité.