volatile (programación de computadoras) - volatile (computer programming)


En la programación informática , en particular en el C , C ++ , C # , y Java lenguajes de programación , la volátil palabra clave indica que un valor puede cambiar entre los diferentes accesos, aunque no parece que ser modificado. Esta palabra clave evita que un compilador optimizador optimice las lecturas o escrituras posteriores y, por lo tanto, reutilice incorrectamente un valor obsoleto u omita escrituras. Los valores volátiles surgen principalmente en el acceso al hardware ( E / S mapeada en memoria ), donde la lectura o escritura en la memoria se usa para comunicarse con dispositivos periféricos , y en el subproceso , donde un subproceso diferente puede haber modificado un valor.

A pesar de ser una palabra clave común, el comportamiento de volatiledifiere significativamente entre los lenguajes de programación y se malinterpreta fácilmente. En C y C ++, es un calificador de tipo , como const, y es una propiedad del tipo . Además, en C y C ++ no funciona en la mayoría de los escenarios de subprocesos, y se desaconseja su uso. En Java y C #, es una propiedad de una variable e indica que el objeto al que está vinculada la variable puede mutar y está específicamente diseñado para subprocesos. En el lenguaje de programación D , hay una palabra clave separada sharedpara el uso de subprocesos, pero no volatileexiste ninguna palabra clave.

En C y C ++

En C, y en consecuencia C ++, la volatilepalabra clave estaba destinada a

  • permitir el acceso a dispositivos de E / S asignados en memoria
  • Permitir el uso de variables entre setjmpylongjmp
  • Permitir el uso de sig_atomic_tvariables en manejadores de señales.

Aunque tanto C como C ++, los estándares de C no expresan que la volatilesemántica se refiere al valor l, no al objeto al que se hace referencia. El informe de defectos respectivo DR 476 (a C11) todavía se está revisando con C17.

Las operaciones sobre las volatilevariables no son atómicas , ni establecen una relación adecuada de suceder antes del subproceso. Esto se especifica en los estándares relevantes (C, C ++, POSIX , WIN32), y las variables volátiles no son seguras para subprocesos en la gran mayoría de las implementaciones actuales. Por lo tanto, volatilemuchos grupos de C / C ++ desaconsejan el uso de palabras clave como mecanismo de sincronización portátil.

Ejemplo de E / S mapeadas en memoria en C

En este ejemplo, el código establece el valor almacenado en fooa 0. Luego comienza a sondear ese valor repetidamente hasta que cambia a 255:

static int foo;

void bar(void) {
    foo = 0;

    while (foo != 255)
         ;
}

Un compilador optimizador notará que ningún otro código puede cambiar el valor almacenado fooy asumirá que permanecerá igual 0en todo momento. Por lo tanto, el compilador reemplazará el cuerpo de la función con un bucle infinito similar a este:

void bar_optimized(void) {
    foo = 0;

    while (true)
         ;
}

Sin embargo, foopodría representar una ubicación que puede ser cambiada por otros elementos del sistema informático en cualquier momento, como un registro de hardware de un dispositivo conectado a la CPU . El código anterior nunca detectaría tal cambio; sin la volatilepalabra clave, el compilador asume que el programa actual es la única parte del sistema que podría cambiar el valor (que es, con mucho, la situación más común).

Para evitar que el compilador optimice el código como se indicó anteriormente, volatilese utiliza la palabra clave:

static volatile int foo;

void bar (void) {
    foo = 0;

    while (foo != 255)
        ;
}

Con esta modificación, la condición del bucle no se optimizará y el sistema detectará el cambio cuando ocurra.

Generalmente, hay operaciones de barrera de memoria disponibles en plataformas (que están expuestas en C ++ 11) que deberían ser preferidas en lugar de volátiles, ya que permiten que el compilador realice una mejor optimización y, lo que es más importante, garantizan un comportamiento correcto en escenarios de subprocesos múltiples; ni la especificación C (antes de C11) ni la especificación C ++ (antes de C ++ 11) especifican un modelo de memoria de subprocesos múltiples, por lo que es posible que los volátiles no se comporten de manera determinista en los sistemas operativos / compiladores / CPU.

Comparación de optimización en C

Los siguientes programas en C y los ensamblados que los acompañan demuestran cómo la volatilepalabra clave afecta la salida del compilador. El compilador en este caso fue GCC .

Al observar el código ensamblador, se ve claramente que el código generado con volatileobjetos es más detallado, lo que lo hace más largo para volatileque se pueda cumplir con la naturaleza de los objetos. La volatilepalabra clave evita que el compilador realice la optimización en el código que involucra objetos volátiles, asegurando así que cada asignación y lectura de variable volátil tenga un acceso a la memoria correspondiente. Sin la volatilepalabra clave, el compilador sabe que no es necesario volver a leer una variable de la memoria en cada uso, porque no debería haber ninguna escritura en su ubicación de memoria desde ningún otro hilo o proceso.

C ++ 11

De acuerdo con el estándar ISO C ++ 11 , la palabra clave volátil solo está pensada para su uso para el acceso al hardware; no lo utilice para la comunicación entre subprocesos. Para la comunicación entre subprocesos, la biblioteca estándar proporciona std::atomic<T>plantillas.

En Java

El lenguaje de programación Java también tiene la volatilepalabra clave, pero se usa para un propósito algo diferente. Cuando se aplica a un campo, el calificador de Java volatileproporciona las siguientes garantías:

  • En todas las versiones de Java, hay un orden global en lecturas y escrituras de todas las variables volátiles (este orden global en volátiles es un orden parcial sobre el orden de sincronización más grande (que es un orden total sobre todas las acciones de sincronización )). Esto implica que cada hilo que acceda a un campo volátil leerá su valor actual antes de continuar, en lugar de (potencialmente) usar un valor almacenado en caché. (Sin embargo, no hay garantía sobre el orden relativo de lecturas y escrituras volátiles con lecturas y escrituras regulares, lo que significa que generalmente no es una construcción de subprocesos útil).
  • En Java 5 o posterior, las lecturas y escrituras volátiles establecen una relación de suceder antes , muy similar a la adquisición y liberación de un mutex.

El uso volatilepuede ser más rápido que un bloqueo , pero no funcionará en algunas situaciones antes de Java 5. La gama de situaciones en las que lo volátil es efectivo se amplió en Java 5; en particular, el bloqueo con doble verificación ahora funciona correctamente.

C ª#

En C # , volatilegarantiza que el código que accede al campo no esté sujeto a algunas optimizaciones no seguras para subprocesos que pueden realizar el compilador, el CLR o el hardware. Cuando se marca un campo volatile, se le indica al compilador que genere una "barrera de memoria" o "valla" a su alrededor, lo que evita el reordenamiento de instrucciones o el almacenamiento en caché vinculado al campo. Al leer un volatilecampo, el compilador genera un cercado de adquisición , que evita que otras lecturas y escrituras en el campo, incluidas las de otros subprocesos, se muevan antes del cercado. Al escribir en un volatilecampo, el compilador genera una barrera de liberación ; esta valla evita que otras lecturas y escrituras en el campo se muevan después de la valla.

Sólo los siguientes tipos se pueden marcar volatile: todos los tipos de referencia, Single, Boolean, Byte, SByte, Int16, UInt16, Int32, UInt32, Char, y todos los tipos enumerados con un tipo de subyacente Byte, SByte, Int16, UInt16, Int32, o UInt32. (No se incluyen valoran estructuras , así como los tipos primitivos Double, Int64, UInt64y Decimal.)

El uso de la volatilepalabra clave no admite campos que se pasan por referencia o variables locales capturadas ; en estos casos, Thread.VolatileReady Thread.VolatileWritedebe utilizarse en su lugar.

En efecto, estos métodos deshabilitan algunas optimizaciones que normalmente realiza el compilador de C #, el compilador JIT o la propia CPU. Las garantías proporcionadas por Thread.VolatileReady Thread.VolatileWriteson un superconjunto de las garantías proporcionadas por la volatilepalabra clave: en lugar de generar un "medio cercado" (es decir, un cercado de adquisición solo evita el reordenamiento de instrucciones y el almacenamiento en caché que viene antes), VolatileReady VolatileWritegenera un "cercado completo" que evitar el reordenamiento de instrucciones y el almacenamiento en caché de ese campo en ambas direcciones. Estos métodos funcionan de la siguiente manera:

  • El Thread.VolatileWritemétodo fuerza la escritura del valor en el campo en el punto de la llamada. Además, cualquier carga y almacenamiento de pedidos de programas anteriores debe ocurrir antes de la llamada VolatileWritey cualquier carga y almacenamiento de pedidos de programas posteriores debe ocurrir después de la llamada.
  • El Thread.VolatileReadmétodo fuerza la lectura del valor en el campo en el punto de la llamada. Además, cualquier carga y almacenamiento de pedidos de programas anteriores debe ocurrir antes de la llamada VolatileReady cualquier carga y almacenamiento de pedidos de programas posteriores debe ocurrir después de la llamada.

Los métodos Thread.VolatileReady Thread.VolatileWritegeneran una Thread.MemoryBarrierbarrera completa llamando al método, que construye una barrera de memoria que funciona en ambas direcciones. Además de las motivaciones para usar una cerca completa dadas anteriormente, un problema potencial con la volatilepalabra clave que se resuelve usando una cerca completa generada por Thread.MemoryBarrieres el siguiente: debido a la naturaleza asimétrica de las medias cercas, un volatilecampo con una instrucción de escritura seguida de una instrucción de lectura aún puede tener el orden de ejecución intercambiado por el compilador. Debido a que las vallas completas son simétricas, esto no es un problema al usarlas Thread.MemoryBarrier.

En Fortran

VOLATILEes parte del estándar Fortran 2003 , aunque la versión anterior lo admitía como una extensión. Hacer todas las variables volatileen una función también es útil para encontrar errores relacionados con el alias .

integer, volatile :: i ! When not defined volatile the following two lines of code are identical
write(*,*) i**2  ! Loads the variable i once from memory and multiplies that value times itself
write(*,*) i*i   ! Loads the variable i twice from memory and multiplies those values

Al "profundizar" siempre en la memoria de un VOLATILE, el compilador de Fortran no puede reordenar las lecturas o escrituras en volátiles. Esto hace visible a otros hilos las acciones realizadas en este hilo y viceversa.

El uso de VOLATILE reduce e incluso puede evitar la optimización.

Referencias

enlaces externos