Va a ser muy difícil reproducir este error. De hecho, iría tan lejos como para decir que nunca serás capaz de reproducirlo usando .NET Framework. La razón es porque la implementación de Microsoft usa un fuerte modelo de memoria para escrituras. Eso significa que las escrituras son tratadas como si fueran volátiles. Una escritura volátil tiene semántica de liberación de bloqueo, lo que significa que todas las escrituras previas deben confirmarse antes de la escritura actual.
Sin embargo, la especificación ECMA tiene un modelo de memoria más débil. Por lo tanto, es teóricamente posible que Mono o incluso una versión futura de .NET Framework comience a exhibir el comportamiento erróneo.
Entonces, lo que estoy diciendo es que es muy poco probable que eliminar las barreras # 1 y # 2 tenga un impacto en el comportamiento del programa. Eso, por supuesto, no es una garantía, sino una observación basada en la implementación actual del CLR solamente.
La eliminación de las barreras n. ° 3 y n. ° 4 definitivamente tendrá un impacto. Esto es bastante fácil de reproducir. Bueno, no este ejemplo per se, pero el siguiente código es una de las demostraciones más conocidas. Tiene que ser compilado usando la compilación Release y se ejecutó fuera del depurador. El error es que el programa no termina. Puede solucionar el error haciendo una llamada al Thread.MemoryBarrier
dentro del lazo while
o marcando 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 razón por la cual algunos errores de roscado son difíciles de reproducir se debe a que las mismas tácticas que utilizan para simular el entrelazado de hilo en realidad puede corregir el error. Thread.Sleep
es el ejemplo más notable porque genera barreras de memoria. Puede verificarlo haciendo una llamada dentro del bucle while
y observando que el error desaparece.
Puede ver mi respuesta here para otro análisis del ejemplo del libro que ha citado.
En el libro también tiene este ejemplo. Podría probar y pasar. –
No repro en vs2015 hasta el momento. – AgentFire
@Brian Gideon Puede ser que sea muy tonto, pero no entiendo por qué el programa de tu ejemplo nunca termina. ¿Por qué 'while (! Stop)' nunca obtener 'false'? ¿Puedes explicar un poco más? ¿O puede sugerir algún blog detallado? Leí el artículo OP publicado (ejemplo de albahari), me confundí también allí; para mí, la barrera 1 es suficiente, ¿por qué hay otras barreras? MSDN dice que 'MemoryBarrier' impide el reordenamiento de las instrucciones. Entonces, ¿por qué solo la barrera 1 no es suficiente (porque 'completed' no se puede ejecutar antes de que se establezca' answer')? Realmente no lo entiendo. – mshsayem