stringtranslate.com

volátil (programación informática)

En programación informática , se dice que un valor es volátil si puede ser leído o modificado de forma asincrónica por algo distinto del hilo de ejecución actual . El valor de una volatilevariable puede cambiar espontáneamente por razones como: compartir valores con otros hilos; compartir valores con controladores de señales asincrónicos ; acceder a dispositivos de hardware a través de E/S mapeadas en memoria (donde puede enviar y recibir mensajes de dispositivos periféricos leyendo y escribiendo en la memoria). El soporte para estos casos de uso varía considerablemente entre los diversos lenguajes de programación que tienen la volatilepalabra clave. La volatilidad puede tener implicaciones con respecto a las convenciones de llamada de funciones y cómo se almacenan, acceden y almacenan en caché las variables.

En C y C++

En C y C++, volatilees un calificador de tipo , como const, y es parte de un tipo (por ejemplo, el tipo de una variable o campo).

El comportamiento de la volatilepalabra clave en C y C++ se da a veces en términos de suprimir optimizaciones de un compilador optimizador : 1- no eliminar volatilelecturas y escrituras existentes, 2- no agregar nuevas volatilelecturas y escrituras, y 3- no reordenar volatilelecturas y escrituras. Sin embargo, esta definición es solo una aproximación para el beneficio de los nuevos estudiantes, y no se debe confiar en esta definición aproximada para escribir código de producción real.

En C, y en consecuencia en C++, la volatilepalabra clave tenía como objetivo: [1]

Los estándares de C y C++ permiten escribir código portátil que comparte valores entre longjmpobjetos volatile, y los estándares permiten escribir código portátil que comparte valores entre manejadores de señales y el resto del código en volatile sig_atomic_tobjetos. Cualquier otro uso de volatilela palabra clave en C y C++ es inherentemente no portátil o incorrecto. En particular, escribir código con la volatilepalabra clave para dispositivos de E/S mapeados en memoria es inherentemente no portátil y siempre requiere un profundo conocimiento de la implementación y la plataforma de C/C++ de destino específicas.

Multi-hilo

Es un error común pensar que la volatilepalabra clave es útil en código multihilo portable en C y C++. La volatilepalabra clave en C y C++ nunca ha funcionado como una herramienta útil y portable para ningún escenario multihilo. [2] [3] [4] [5] A diferencia de los lenguajes de programación Java y C# , las operaciones sobre volatilevariables en C y C++ no son atómicas , y las operaciones sobre volatilevariables no tienen suficientes garantías de ordenamiento de memoria (es decir, barreras de memoria ). La mayoría de los compiladores, enlazadores y tiempos de ejecución de C y C++ simplemente no proporcionan las garantías de ordenamiento de memoria necesarias para que la volatilepalabra clave sea útil para cualquier escenario multihilo. Antes de los estándares C11 y C++11, los programadores se veían obligados a confiar en las garantías de las implementaciones y plataformas individuales (por ejemplo, POSIX y WIN32) para escribir código multihilo . Con los estándares modernos C11 y C++11, los programadores pueden escribir código multihilo portable utilizando nuevas construcciones portables como las std::atomic<T>plantillas. [6]

Ejemplo de E/S mapeada en memoria en C

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

int estático foo ;  barra vacía ( vacío ) { foo = 0 ;      mientras ( foo != 255 ) ; }    

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

vacío bar_optimized ( vacío ) { foo = 0 ;      mientras ( verdadero ) ; }  

Sin embargo, el programador puede hacer fooreferencia a otro elemento del sistema informático, como un registro de hardware de un dispositivo conectado a la CPU , que puede cambiar el valor de foomientras se ejecuta este código. (Este ejemplo no incluye los detalles sobre cómo hacer fooreferencia a un registro de hardware de un dispositivo conectado a la CPU). Sin la volatilepalabra clave, un compilador optimizador probablemente convertirá el código de la primera muestra con la lectura en el bucle a la segunda muestra sin la lectura en el bucle como parte de la optimización de movimiento de código invariante de bucle común y, por lo tanto, es probable que el código nunca note el cambio que está esperando.

Para evitar que el compilador realice esta optimización, volatilese puede utilizar la palabra clave:

estático volátil int foo ;   barra vacía ( vacío ) { foo = 0 ;       mientras ( foo != 255 ) ; }    

La volatilepalabra clave evita que el compilador mueva la lectura fuera del bucle y, por lo tanto, el código notará el cambio esperado en la variable foo.

Comparación de optimización en C

Los siguientes programas en C y los extractos en lenguaje ensamblador 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 que volatilese 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, lo que garantiza que cada asignación y lectura de variable volátil tenga un acceso de memoria correspondiente. Sin la volatilepalabra clave, el compilador sabe que no es necesario volver a leer una variable desde 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.

Defectos de las normas

Si bien tanto C como C++ lo tienen previsto, el estándar C actual no expresa que la volatilesemántica se refiere al valor l, no al objeto referenciado. El informe de defectos correspondiente DR 476 (a C11) todavía está bajo revisión con C17 . [7]

Defectos del compilador

A diferencia de otras características del lenguaje C y C++, la volatilepalabra clave no está bien soportada por la mayoría de las implementaciones de C/C++, incluso para usos portables de acuerdo con los estándares de C y C++. La mayoría de las implementaciones de C/C++ tienen errores en cuanto al comportamiento de la volatilepalabra clave. [8] [9] Los programadores deben tener mucho cuidado siempre que utilicen la volatilepalabra clave en C y C++.

En Java

En todas las versiones modernas del lenguaje de programación Java , la volatilepalabra clave ofrece las siguientes garantías:

En conjunto, estas garantías forman volatileuna construcción multihilo útil en Java . En particular, el algoritmo de bloqueo de doble verificaciónvolatile típico funciona correctamente en Java . [12]

Versiones muy antiguas de Java

Antes de la versión 5 de Java, el estándar Java no garantizaba el orden relativo de volatilelas operaciones de volatilelectura y escritura. En otras palabras, volatileno tenía semántica de barrera de memoria de "adquisición" y "liberación" . Esto limitaba en gran medida su uso como una construcción de subprocesos múltiples . En particular, el algoritmo de bloqueo de doble verificaciónvolatile típico no funcionaba 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 ser realizadas por el compilador, el CLR o por 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 la reordenación o el almacenamiento en caché de instrucciones vinculadas al campo. Al leer un volatilecampo, el compilador genera una acquire-fence , que evita que otras lecturas y escrituras en el campo se muevan antes de la valla. Al escribir en un volatilecampo, el compilador genera una release-fence ; esta valla evita que otras lecturas y escrituras en el campo se muevan después de la valla. [13]

Solo se pueden marcar los siguientes tipos volatile: todos los tipos de referencia, Single, Boolean, Byte, SByte, Int16, UInt16, Int32, UInt32, Char, y todos los tipos enumerados con un tipo subyacente de Byte, SByte, Int16, UInt16, Int32o UInt32. [14] ( Esto excluye las estructuras de valor , así como los tipos primitivos Double, y ).Int64UInt64Decimal

El uso de la volatilepalabra clave no admite campos que se pasan por referencia o variables locales capturadas ; en estos casos, se debe utilizar Thread.VolatileReady en su lugar. [13]Thread.VolatileWrite

En efecto, estos métodos desactivan algunas optimizaciones que normalmente realizan 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 una "media valla" (es decir, una valla de adquisición solo impide la reordenación y el almacenamiento en caché de las instrucciones que vienen antes de ella), VolatileReady VolatileWritegeneran una "valla completa" que impide la reordenación y el almacenamiento en caché de las instrucciones de ese campo en ambas direcciones. [13] Estos métodos funcionan de la siguiente manera: [15]

Los métodos Thread.VolatileReady Thread.VolatileWritegeneran una cerca completa al llamar al Thread.MemoryBarriermé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 al usar 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 cercas completas son simétricas, esto no es un problema cuando se usa Thread.MemoryBarrier. [13]

En Fortran

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

entero , volátil :: i ! Cuando no se define como volátil, las dos líneas de código siguientes son idénticas. write ( * , * ) i ** 2 ! Carga la variable i una vez desde la memoria y multiplica ese valor por sí mismo. write ( * , * ) i * i ! Carga la variable i dos veces desde la memoria y multiplica esos valores.        

Al "explorar" siempre la memoria de un VOLÁTIL, se evita que el compilador de Fortran reordene las lecturas o escrituras en los volátiles. Esto hace visibles para otros subprocesos las acciones realizadas en este subproceso, y viceversa. [17]

El uso de VOLATILE reduce e incluso puede impedir la optimización. [18]

Referencias

  1. ^ "Publicación del comité de estándares de C++".
  2. ^ "Palabra clave volátil en Visual C++". Microsoft MSDN . 21 de septiembre de 2021.
  3. ^ "Documentación del kernel de Linux: por qué no se debe utilizar la clase de tipo "volátil"". kernel.org .
  4. ^ Scott Meyers; Andrei Alexandrescu (2004). "C++ y los peligros del bloqueo con doble comprobación" (PDF) . DDJ .
  5. ^ Jeremy Andrews (2007). "Linux: Volatile Superstition". kerneltrap.org. Archivado desde el original el 20 de junio de 2010. Consultado el 9 de enero de 2011 .
  6. ^ "volátil (C++)". Microsoft MSDN . 21 de septiembre de 2021.
  7. ^ Resumen de la solicitud de aclaración para C11. Versión 1.13, octubre de 2017.
  8. ^ Eide, Eric; Regehr, John (octubre de 2008). "Los volátiles se compilan incorrectamente y qué hacer al respecto" (PDF) . Actas de la octava conferencia internacional de ACM e IEEE sobre software integrado (EMSOFT), Atlanta, Georgia, EE. UU. , a través de cs.utah.edu.
  9. ^ "Errores volátiles, tres años después: incorporados en la academia". blog.regehr.org . Consultado el 28 de agosto de 2024 .
  10. ^ Sección 17.4.4: Orden de sincronización "Especificación del lenguaje Java®, Java SE 7 Edition". Oracle Corporation . 2013 . Consultado el 12 de mayo de 2013 .
  11. ^ "Concurrencia en Java: comprensión de la palabra clave 'volátil'". dzone.com. 8 de marzo de 2021. Archivado desde el original el 9 de mayo de 2021. Consultado el 9 de mayo de 2021 .
  12. ^ Neil Coffey. "Bloqueo con doble verificación (DCL) y cómo solucionarlo". Javamex . Consultado el 19 de septiembre de 2009 .
  13. ^ abcd Albahari, Joseph. "Parte 4: Subprocesamiento avanzado". Subprocesamiento en C# . O'Reilly Media. Archivado desde el original el 12 de diciembre de 2019 . Consultado el 9 de diciembre de 2019 .{{cite web}}: CS1 maint: bot: estado de URL original desconocido ( enlace )
  14. ^ Richter, Jeffrey (11 de febrero de 2010). "Capítulo 7: Constantes y campos". CLR Via C# . Microsoft Press. pp. 183. ISBN 978-0-7356-2704-8.
  15. ^ Richter, Jeffrey (11 de febrero de 2010). "Capítulo 28: Construcciones de sincronización de subprocesos primitivos". CLR Via C# . Microsoft Press. págs. 797–803. ISBN 978-0-7356-2704-8.
  16. ^ "Atributo y declaración VOLATILE". Cray. Archivado desde el original el 23 de enero de 2018. Consultado el 22 de abril de 2016 .
  17. ^ "Matriz volátil y compartida en Fortran". Intel.com .
  18. ^ "VOLÁTIL". Oracle.com .

Enlaces externos