c++ multithreading concurrency volatile

c++ - Cuándo usar volátil con multi threading?



multithreading concurrency (3)

Necesitas volátiles y posiblemente bloqueo.

volátil le dice al optimizador que el valor puede cambiar de forma asíncrona, por lo tanto

volatile bool flag = false; while (!flag) { /*do something*/ }

leerá la bandera cada vez que esté en el ciclo.

Si desactiva la optimización o hace que cada variable sea volátil, un programa se comportará igual pero más lento. volátil solo significa ''Sé que acabas de leerlo y saber lo que dice, pero si digo leerlo, léelo.

Locking es parte del programa. Así que, por cierto, si está implementando semáforos, entonces, entre otras cosas, debe ser volátil. (No lo intentes, es difícil, probablemente necesites un pequeño ensamblador o las nuevas cosas atómicas, y ya está hecho).

Si hay dos subprocesos que acceden a una variable global, muchos tutoriales dicen que la variable es volátil para evitar que el compilador guarde en caché la variable en un registro y, por lo tanto, no se actualice correctamente. Sin embargo, dos subprocesos que acceden a una variable compartida son algo que requiere protección a través de un mutex, ¿no? Pero en ese caso, entre el bloqueo del hilo y la liberación del mutex, el código está en una sección crítica donde solo ese hilo puede acceder a la variable, en cuyo caso la variable no necesita ser volátil.

Entonces, ¿cuál es el uso / propósito de volátil en un programa multiproceso?


Volatile ocasionalmente es útil por el siguiente motivo: este código:

/* global */ bool flag = false; while (!flag) {}

está optimizado por gcc para:

if (!flag) { while (true) {} }

Lo cual es obviamente incorrecto si el otro hilo escribe la bandera. Tenga en cuenta que sin esta optimización, el mecanismo de sincronización probablemente funcione (dependiendo del otro código pueden necesitarse algunas barreras de memoria) - no es necesario un mutex en 1 productor - 1 escenario de consumidor.

De lo contrario, la palabra clave volátil es demasiado extraña para ser utilizable: no proporciona garantías de ordenación de memoria con accesos volátiles y no volátiles y no proporciona ninguna operación atómica, es decir, no obtiene ayuda del compilador con palabra clave volátil excepto el almacenamiento en caché de registro deshabilitado. .


Respuesta breve y rápida : volatile es (casi) inútil para la programación de aplicaciones multiproceso y agnóstica de plataforma. No proporciona ninguna sincronización, no crea vallas de memoria, ni garantiza el orden de ejecución de las operaciones. No hace que las operaciones sean atómicas. No hace que tu código sea mágicamente seguro. volatile puede ser la instalación más incomprendida en todo C ++. Ver this , this y this para obtener más información sobre volatile

Por otro lado, volatile tiene algún uso que puede no ser tan obvio. Se puede usar de la misma manera que uno usaría const para ayudar al compilador a mostrarle dónde podría estar cometiendo un error al acceder a algún recurso compartido de una manera no protegida. Este uso es discutido por Alexandrescu en este artículo . Sin embargo, esto es básicamente el uso del sistema de tipo C ++ de una manera que a menudo se ve como una invención y puede evocar un comportamiento indefinido.

volatile fue diseñado específicamente para ser utilizado cuando se hace interfaz con hardware mapeado en memoria, manejadores de señal y la instrucción de código de máquina setjmp. Esto hace que los volatile sean directamente aplicables a la programación a nivel de sistemas en lugar de la programación a nivel de aplicaciones normales.

El estándar C ++ de 2003 no dice que volatile aplica ningún tipo de semántica de adquisición o liberación en las variables. De hecho, el estándar es completamente silencioso en todos los asuntos de multihilo. Sin embargo, las plataformas específicas aplican la semántica de Adquisición y Liberación en variables volatile .

[Actualización para C ++ 11]

El estándar C ++ 11 ahora reconoce el subprocesamiento múltiple directamente en el modelo de memoria y en el idioma, y ​​proporciona servicios de biblioteca para manejarlo de manera independiente de la plataforma. Sin embargo, la semántica de volatile todavía no ha cambiado. volatile aún no es un mecanismo de sincronización. Bjarne Stroustrup lo dice en TCPPPL4E:

No use volatile excepto en el código de bajo nivel que trata directamente con el hardware.

No asuma que volatile tiene un significado especial en el modelo de memoria. No es asi. No es, como en algunos idiomas posteriores, un mecanismo de sincronización. Para obtener la sincronización, use atomic , mutex o condition_variable .

[/ Fin actualización]

Todo lo anterior aplica el lenguaje C ++ en sí mismo, tal como lo define el Estándar 2003 (y ahora el Estándar 2011). Sin embargo, algunas plataformas específicas agregan funcionalidad o restricciones adicionales a lo que hace volatile . Por ejemplo, en MSVC 2010 (al menos) la semántica de Adquirir y Liberar se aplica a ciertas operaciones en variables volatile . Desde MSDN :

Al optimizar, el compilador debe mantener el orden entre las referencias a los objetos volátiles, así como las referencias a otros objetos globales. En particular,

Una escritura en un objeto volátil (escritura volátil) tiene semántica de lanzamiento; una referencia a un objeto global o estático que ocurre antes de que se escriba una escritura en un objeto volátil en la secuencia de instrucciones antes de esa escritura volátil en el binario compilado.

Una lectura de un objeto volátil (lectura volátil) tiene Semántica de adquisición; una referencia a un objeto global o estático que se produce después de una lectura de memoria volátil en la secuencia de instrucciones se producirá después de esa lectura volátil en el binario compilado.

Sin embargo, puede tomar nota del hecho de que si sigue el enlace anterior, existe cierto debate en los comentarios acerca de si realmente se aplica la semántica de adquisición / liberación en este caso.