variable language c# .net multithreading volatile

language - ¿Por qué C#volatile no protege la reescritura de escritura y lectura?



volatile c# (3)

La prevención de volatile write con reordenación de volatile read será bastante costosa en el arco x86 / x64. Eso es debido a la optimización de escritura llamada store buffers . Java fue de esta manera y las escrituras volátiles en Java son efectivamente barreras de memoria completa en el nivel de instrucciones de la CPU.

Según este libro en línea , la palabra clave volatile en C # no protege contra la reordenación de las operaciones de escritura seguidas de las operaciones de lectura. Da este ejemplo en el que tanto a como b pueden terminar siendo configurados a 0 , a pesar de que x e y son volatile :

class IfYouThinkYouUnderstandVolatile { volatile int x, y; void Test1() // Executed on one thread { x = 1; // Volatile write (release-fence) int a = y; // Volatile read (acquire-fence) ... } void Test2() // Executed on another thread { y = 1; // Volatile write (release-fence) int b = x; // Volatile read (acquire-fence) ... } }

Esto parece encajar con lo que dice la especificación en 10.5.3:

Una lectura de un campo volátil se llama lectura volátil. Una lectura volátil tiene "adquirir semántica"; es decir, se garantiza que ocurra antes de cualquier referencia a la memoria que ocurra después de esta en la secuencia de instrucciones.

Una escritura de un campo volátil se llama escritura volátil. Una escritura volátil tiene "liberación semántica"; es decir, se garantiza que sucederá después de cualquier referencia de memoria antes de la instrucción de escritura en la secuencia de instrucciones.

¿Cuál es la razón para esto? ¿Hay algún caso de uso en el que no nos importe que las operaciones de lectura y escritura se reordenen?


Tal vez demasiado tarde para responder ... pero estaba mirando alrededor y vi esto. La lectura volátil es en realidad una llamada a un método similar al siguiente:

public static int VolatileRead(ref int address) { int num = address; Thread.MemoryBarrier(); return num; }

Y la escritura volátil es así:

public static int VolatileWrite(ref int address, int value) { Thread.MemoryBarrier(); adrdress = value; }

La instrucción MemoryBarrier(); Es el que impide reordenar. MemoryBarrier(); asegura que las instrucciones antes se ejecuten antes que las instrucciones después. Cuando VW luego VR, tendrás:

Thread.MemoryBarrier(); adrdress = value; //this line may be reordered with the one bellow int num = address;//this line may be reordered with the one above Thread.MemoryBarrier(); return num;


Volatile no garantiza que las lecturas y las escrituras no se vuelvan a ordenar, solo garantiza que las lecturas obtengan el valor más actualizado (no en caché).

http://msdn.microsoft.com/en-us/library/x13ttww7%28v=vs.71%29.aspx

El sistema siempre lee el valor actual de un objeto volátil en el punto en que se solicita, incluso si la instrucción previa solicitó un valor del mismo objeto. Además, el valor del objeto se escribe inmediatamente en la asignación.

El modificador volátil se usa generalmente para un campo al que se accede mediante varios subprocesos sin usar la instrucción de bloqueo para serializar el acceso. El uso del modificador volátil garantiza que un subproceso recupera el valor más actualizado escrito por otro subproceso.

Siempre que tenga varias operaciones dependientes, necesita usar algún otro mecanismo de sincronización. Por lo general, usar el lock , es más fácil y solo crea cuellos de botella en el rendimiento cuando se abusa o en situaciones muy extremas.