Como se ha demostrado en esta respuesta recientemente he publicado, me parece estar confundido acerca de la utilidad (o falta de ella) de volatile en multi-threaded contextos de programación.

Mi entender es esto: cada vez que una variable puede cambiar fuera de el flujo de control de un trozo de código de acceso a la misma, que la variable debe ser declarada volatile. Manejadores de señales de e/S de registros y variables modificado por otro hilo constituyen este tipo de situaciones.

Por lo tanto, si usted tiene un global int foo, y foo es leído por un hilo y establecer atómicamente por otro hilo (probablemente el uso de una adecuada instrucción de la máquina), el subproceso de lectura ve esta situación en la misma forma en que se ve una variable ajustado por un manejador de señal o modificado por un hardware externo condición y por lo tanto foo debe ser declarada volatile (o, para multiproceso situaciones, consultado con la memoria-cercado de carga, que es probablemente una mejor solución).

Cómo y dónde estoy equivocado?

  • Todos los volátiles que hace es decir que el compilador no debe almacenar en caché el acceso a una variable volátil. No dice nada acerca de serialising dicho acceso. Esto ha sido discutido aquí no sé cuántas veces, y no creo que esta pregunta va a agregar nada a esos debates.
  • Y, sin embargo, de nuevo, una pregunta que no se lo merece, y se ha pedido aquí muchas veces antes de que obtiene upvoted. Por favor, dejar de hacer esto.
  • He buscado otras preguntas, y encontré uno, pero cualquier explicación existente vi que de alguna manera no desencadenar lo que se necesita para realmente entender por qué me había equivocado. Esta pregunta ha generado tal respuesta.
  • Para un gran estudio en profundidad sobre lo que Cpu con datos (a través de sus cachés) hora de salida: rdrop.com/users/paulmck/scalability/paper/whymb.2010.06.07c.pdf
  • En Java volatile crea una barrera de memoria cuando lo lea, así que puede ser utilizado como una bandera de subprocesos que un método ha terminado, ya que exige un pasa-antes de que la relación con el código antes de que la bandera estaba establecido. Este no es el caso en C.
  • Java volátiles no tiene nada que ver con C/C++ volátiles: Java volátiles se define dentro del modelo de lenguaje, puede ser transformado, optimizado (escribir seguido por la lectura de un Java volátil puede ser optimizado para escribir) y volátiles completamente eliminado si la volatilidad de la variable se puede probar que no se comparten entre los hilos. En C/C++ volátil está fuera del modelo de lenguaje, las operaciones de los volátiles los objetos pueden tener efectos visible para otros dispositivos y, por definición, no hay transformación de los volátiles es posible, ni siquiera una lectura cuyo resultado es ignorado pueden ser eliminados. Ellos no tienen el mismo propósito.
  • Que es lo que quiero decir con «no es el caso en C», donde puede ser utilizado para escribir en los registros de hardware, etc., y no se usa para el multithreading, como es comúnmente usado en Java.

9 Comentarios

  1. 205

    El problema con volatile en un multiproceso contexto es que no proporciona todos las garantías que necesitamos. Tiene un par de propiedades que necesitamos, pero no todas, así que no podemos confiar en volatile solo.

    Sin embargo, los primitivos tendríamos que utilizar para el restantes propiedades proporcionan también los que volatile hace, por lo que efectivamente es innecesario.

    Para el hilo de seguridad de los accesos a los datos compartidos, tenemos una garantía de que:

    • la lectura/escritura que realmente sucede (que el compilador no sólo almacenar el valor en un registro en su lugar y aplazar la actualización de la memoria principal hasta mucho más tarde)
    • que no reordenación se lleva a cabo. Supongamos que utilizamos una volatile variable como una bandera para indicar si o no algunas de datos está listo para ser leído. En nuestro código, simplemente establezca el indicador después de la preparación de los datos, de manera que todos se ve bien. Pero ¿qué pasa si las instrucciones son reordenados por lo que el indicador se establece primera?

    volatile garantiza el primer punto. También garantiza que no se produce el reordenamiento de entre los diferentes volátiles lee/escribe. Todos volatile accesos a la memoria se producirá en el orden en el que están especificados. Eso es todo lo que necesitamos para lo que volatile está destinado a: la manipulación de registros de e/S o de hardware asignado en memoria, pero no nos ayuda en multiproceso código donde la volatile objeto a menudo sólo se utiliza para sincronizar el acceso a datos no volátil. Los accesos todavía se pueden reordenar en relación a la volatile queridos.

    La solución para evitar que la reordenación es el uso de un barrera de memoria, que indica al compilador y el CPU que no hay memoria de acceso se pueden reordenar a través de este punto de. La colocación de tales barreras alrededor de nuestro volátiles variable de acceso se asegura de que incluso los no-volátil accesos no ser reorganizado a través de la volátiles uno, lo que nos permite escribir código seguro de subprocesos.

    Sin embargo, barreras de memoria también asegurarse de que todos los pendientes de lecturas/escrituras se ejecutan cuando la barrera se alcanza, por lo que nos da todo lo que necesitamos por sí mismo, haciendo volatile innecesarios. Podemos acaba de quitar el volatile calificador completamente.

    Desde C++11, atómica variables (std::atomic<T>) nos da todas las garantías.

    • Gracias. La pieza que me faltaba en mi entendimiento, tan cerca que puedo decir, fue que los volátiles no garantiza la inexistencia de reordenamiento con los vecinos de la memoria no volátil de accesos, lo que se requiere para la atómica de la bandera de la configuración de caso de uso.
    • ¿Realmente garantizar el orden y prohibir el uso de los valores almacenados en la caché o sólo lo hace en el compilador de nivel? Si el anterior era cierto, usted podría escribir portátil de sincronización de subprocesos, pero todo el código que he visto los usos de la CPU instrucciones específicas y se supone que la CPU va a reordenar todo.
    • Que «ella» se están preguntando acerca de? Volátiles o barreras de memoria? En cualquier caso, la respuesta es prácticamente el mismo. Ambos tienen que trabajar tanto en el compilador y el nivel del CPU, ya que describen el comportamiento observable del programa — así que ellos tienen que asegurarse de que la CPU no reordenar todo, la modificación de la conducta son la garantía. Pero en la actualidad no se puede escribir portátil de sincronización de subprocesos, porque la memoria que las barreras no son parte del estándar de C++ (para portátiles), y volatile no es lo suficientemente fuerte como para ser útil.
    • Una de MSDN ejemplo hace esto, y afirma que las instrucciones no pueden ser reordenados pasado un volátil de acceso: msdn.microsoft.com/en-us/library/12a04hfd(v=vs 80).aspx
    • Pero Microsoft compilador redefine volatile a ser una memoria completa de la barrera (prevención de reordenamiento). Eso no es parte de la norma, por lo que no puede confiar en que este comportamiento en el código portable.
    • Así que, ¿cómo barreras de memoria ayuda cuando hay acceso simultáneo de múltiples CPUs?
    • En resumen, la CPU y el compilador de la magia (y sí, usted necesita para que funcione). Una barrera de memoria es una instrucción del procesador que está diseñado para resolver el problema. Interactúa con el bus de memoria, asegurando que ningún otro núcleo puede leer/escribir en la memoria hasta que la barrera se ha terminado de hacer su cosa, y la CPU no sabe para reordenar los accesos a la memoria a través de ella. Ella realmente no puede ser emulado por software.
    • Pero usted todavía necesita la palabra clave volatile para garantizar el valor como se ve por el código es el valor en la memoria. Sin ella, no podía el optimizador de memoria caché el valor en un registro? El problema se convierte entonces en uno de acceso concurrente.
    • no, que es donde el compilador «magia» de parte de la ecuación que viene. Una barrera de memoria ha de entenderse por por tanto la CPU y el compilador. Si el compilador entiende que la semántica de una barrera de memoria, sabe evitar los trucos que (así como la reordenación de las lecturas/escrituras a través de la barrera). Y por suerte, el compilador no entender la semántica de una barrera de memoria, así que al final, todo funciona. 🙂
    • Así que ¿cómo se puede especificar cuando un recuerdo se requiere una barrera? El compilador no tiene manera de saber si el código se va a ejecutar en un solo core/hilo o varios núcleos/hilos. El optimizador de conversiones sería registerise valores siempre que sea posible, que sería un error si dos núcleos, el acceso a los datos simultáneamente, ya que cada hilo/core tiene su propio registro almacenado versión de el valor. Sin ‘volátil’ el compilador, ciertamente, se equivocan.
    • No estoy seguro de entender. Especifique una barrera de memoria mediante el uso de lo intrínseco, el compilador proporciona para el propósito (o mediante el uso de C++11 equivalente). Es algo que tienes que insertar manualmente; usted no puede confiar en el compilador de la generación de barreras de memoria donde sea necesario para usted.
    • Veo, no era claro en su respuesta que las barreras de memoria son una extensión del lenguaje / C++11 característica. Así que si el compilador no tiene la extensión que estás atascado con volátil. Pensé que había algo en C/C++ (pre-11) que estaba inconsciente.
    • Lo que yo veo. Lo siento por la confusión, a continuación,. Sin embargo, si el compilador no tiene tal extensión, entonces estás de suerte de todos modos, porque volatile no es suficiente en sí mismo.
    • Hilos de sí mismos son siempre una plataforma dependiente de la extensión antes de C++11 y C11. A mi conocimiento, que cada C y C++ entorno que proporciona un roscado extensión también proporciona una «barrera de memoria» de extensión. Independientemente, volatile es siempre inútil para multi-roscado de programación. (Excepto en Visual Studio, donde volátiles es la memoria de la barrera de la extensión).
    • Como usted dijo, la volatilidad es útil para acceder a registros de e/S. El costo es mucho mayor si utilizamos la memoria de la barrera para implementar dicha función (bloquear posibles compilador/optimización de hardware)
    • ¿puede explicar eso? ¿Por qué el costo será más alto que el de volatile? volatile impide optimizaciones demasiado.
    • Si nos barrera de memoria para simular e implementar la función de la volatilidad, sería la causa de muchos otros no relacionados de lectura/escritura a suceder realmente? También bloquea el compilador para reordenar el acceso a todas las variables independientes.
    • No veo por qué lo haría «muchos otros no relacionados de lectura/escritura». El punto sobre la reordenación puede influir de dos formas, dependiendo del uso. Una memoria impide que todo lee/escribe de ser reorganizado a través de ella, pero sólo en un punto específico en el tiempo (Cuando la barrera se ejecuta el código), mientras que la volatilidad de la variable sólo impide la reordenación con respecto a otros volátiles lee/escribe, pero, por otro lado, impide que este para todo de los accesos a la variable, no sólo en un punto específico en el código.
    • Según mi entendimiento, la volatilidad va a obligar a leer el valor de la memoria en lugar de la memoria caché. Supongamos que tenemos una variable compartida (entre hilos), un hilo modifica y la variable es no volátil. Suponiendo que estamos utilizando el mutex bloquea cada vez que se accede a la variable compartida. Dado que la variable compartida no es volátil, cómo se garantiza que la variable siempre se leen de la memoria, no de la caché?
    • eso está implícito en la exclusión mutua. Esto implica una barrera de memoria, así que cuando es ejecutado, el compilador es instruido para asegurarse de que todas las escrituras a las variables que pueden ser visibles para otras roscas deben ser eliminados de la memoria.
    • Palabra clave Volatile es todavía necesaria para evitar que el compilador la caché de la bandera en lugar de leer el valor de la memoria.
    • No, No es así, los datos de la dependencia de análisis trata de la barrera de memoria como una función externa que podría haber cambiado alguna variable que ha sido un alias. (Registro de almacenamiento de variables locales cuya dirección es tomado nunca en realidad es perfectamente seguro). Incluso en un único subproceso código, global_x = 5; extern_call(); cout << global_x; el compilador no puede reemplazar con cout << 5; porque extern_call() puede haber cambiado el valor.
    • ¿En qué casos sería el tratamiento de volatile como un global de barrera para el compilador de reordenación representan un importante problema de rendimiento? Si dentro de una pieza de código de todos los lugares que necesitan barreras ellos marcados explícitamente, lo que permite un compilador para reordenar las operaciones a través de volatile podría tener algún beneficio, pero uno tiene un código que utiliza volatile pero no está personalizado para el uso de barreras entendido por el compilador uno está usando, una opción para el tratamiento de volatile como un compilador de pedidos barrera permitiría que el compilador útil ejecutar dicho código.
    • Usted dice volátiles que no es necesario – lo que implica que una barrera de memoria obligaría a lee de la memoria, y no de la memoria caché valor del registro. Es eso correcto, que la memoria de las barreras de la fuerza de lee no se almacena? O ¿significa esto para multi-threaded uso de volátiles, no registro de e/s de uso?
    • En las Cpu modernas (últimos diez años), volatile ni siquiera la fuerza de una lectura o escritura en la memoria. Sólo hace que el CPU para que se comporten como si de una lectura o de escritura fue forzado a la memoria de un único subproceso código. Y eso es todo lo que la norma requiere, ya que no había multi-threaded semántica de los requisitos.
    • Las únicas ventajas de volatile más de instrinsics asumir fuerzas externas puede leer y/o escribir cualquier objeto en cualquier punto en el tiempo (1) código existente que no incluye volátiles ni barreras pueden ser fijos más fácilmente, en la mayoría de los casos, mediante la adición de volatile que mediante la adición de barreras; (2) algunas de las plataformas requieren instrucciones especiales para los volátiles de lecturas/escrituras que son diferentes de hacer un sync, el acceso y, a continuación, otro de sincronización. El único cuerdo razón por la que puedo pensar para C89 no se han incluido las barreras (desde cualquier aplicación debe ser capaz de lograr la necesaria…
    • …la semántica, si es o no realmente requieren el uso de barreras para para para), es que los autores se espera que las implementaciones donde los programadores pueden necesitar barreras para diversos fines haría «volátil» proporcionan). Incluso registros de e/S a menudo no necesitan semántica tan preciso como lo volatile proporcionaría. En muchos casos, será importante que un grupo de operaciones precede a otro, pero el orden de las operaciones dentro de cada grupo, no importa.

  2. 47

    También podría considerar esto desde el La Documentación Del Kernel De Linux.

    Los programadores de C a menudo han tomado volátil significa que la variable
    podría ser cambiado fuera de la corriente del hilo de ejecución; como un
    resultado, que a veces se ven tentados a utilizar en el código del kernel cuando
    las estructuras de datos compartidos están siendo utilizados. En otras palabras, han sido
    sabido tratar a los volátiles tipos como una especie de fácil atómica variable, la cual
    ellos no lo son. El uso de volátiles en el código del núcleo es casi nunca
    correcto; en este documento se describe por qué.

    El punto clave para entender con respecto a la volatilidad, es que su
    el propósito es suprimir la optimización, que casi nunca es lo que uno
    realmente quiere hacer. En el núcleo, se debe proteger de datos compartidos
    estructuras en contra de los no deseados de acceso simultáneo, que es mucho más que un
    tarea diferente. El proceso de protección contra los no deseados
    la concurrencia también evitar casi todos optimización de problemas relacionados con el
    en una forma más eficiente.

    Como volátil, el núcleo primitivas que hacen que el acceso simultáneo a
    la seguridad de los datos (spinlocks, mutexes, barreras de memoria, etc.) están diseñados para
    evitar los indeseados de optimización. Si se utilizan correctamente, no
    habrá necesidad de utilizar sustancias volátiles así. Si volátil es todavía
    es necesario, no es casi seguramente un error en el código de algún lugar. En
    correctamente escrito el código del núcleo, la volatilidad sólo puede servir para cosas lentas
    abajo.

    Considerar un típico bloque de código del kernel:

    spin_lock(&the_lock);
    do_something_on(&shared_data);
    do_something_else_with(&shared_data);
    spin_unlock(&the_lock);

    Si todo el código sigue el bloqueo de las reglas, el valor de shared_data
    no se puede cambiar inesperadamente mientras the_lock se lleva a cabo. Cualquier otro código
    que puede querer jugar con los datos estarán esperando en la cerradura.
    El spinlock primitivas de actuar como barreras de memoria – que explícitamente se
    escrito para hacerlo – lo que significa que los datos de accesos no será optimizada
    a través de ellos. Por lo que el compilador podría pensar que sabe lo que va a ser en
    shared_data, pero la spin_lock() la llamada, ya que actúa como una memoria
    barrera, le obligan a olvidar nada se sabe. No habrá
    problemas de optimización con accesos a los datos.

    Si shared_data fueron declarados volátiles, el bloqueo sería todavía
    es necesario. Pero el compilador también sería impedido a partir de la optimización de
    el acceso a shared_data dentro de de la sección crítica, cuando sabemos que
    nadie puede estar trabajando con él. Mientras se mantiene el bloqueo,
    shared_data no es volátil. Cuando se trata con datos compartidos, adecuada
    bloqueo hace volátiles innecesarios y potencialmente dañinos.

    El almacenamiento volátil clase fue pensado originalmente para la memoria de e/S asignada
    de los registros. Dentro del núcleo, registro de accesos, también, debe ser
    protegido por los bloqueos, pero uno no quiere que el compilador
    «optimizar» registro de accesos dentro de una sección crítica. Pero, dentro de
    el núcleo, memoria de e/S de accesos se realiza siempre a través de descriptor de acceso
    funciones; el acceso a memoria de e/S directamente a través de punteros es frunció el ceño
    y no funciona en todas las arquitecturas. Los descriptores de acceso son
    escrito para evitar los indeseados de optimización, por lo que, una vez más, la volatilidad es
    innecesarias.

    Otra situación en la que uno podría estar tentado a usar volátil es cuando
    el procesador está ocupado-esperar en el valor de una variable. El derecho
    forma de realizar una espera activa es:

    while (my_variable != what_i_want)
        cpu_relax();

    La cpu_relax() la llamada puede reducir el consumo de energía de la CPU o el rendimiento de una
    hyperthreaded doble procesador; también pasa a servir como una memoria
    barrera, por lo que, una vez más, la volatilidad es innecesario. Por supuesto,
    ocupado-esperar es generalmente un anti-social de la ley, para empezar.

    Todavía hay unas pocas situaciones donde volátil tiene sentido en
    el núcleo:

    • Las mencionadas funciones de descriptor de acceso puede utilizar volátiles en
      arquitecturas donde direct I/O de acceso a la memoria no funciona. Esencialmente,
      cada descriptor de acceso de llamada se convierte en un poco de la sección crítica por su propia cuenta y
      asegura que el acceso sucede como se espera por el programador.

    • Código ensamblador en línea que cambia de memoria, pero que no tiene otro
      efectos secundarios visibles, corre el riesgo de ser eliminado por GCC. La adición de la volátiles
      palabra clave a asm, que las declaraciones de prevenir esta eliminación.

    • La jiffies variable es especial, ya que puede tener un valor diferente
      cada vez que se hace referencia, pero se puede leer sin ningún tipo especial de
      el bloqueo. Así jiffies puede ser volátil, pero la adición de otros
      las variables de este tipo es fuertemente criticados. Jiffies se considera
      para ser un «estúpido legado» edición (Linus palabras) en este sentido; la fijación
      sería más problemas de lo que vale.

    • Punteros a estructuras de datos en memoria coherente que puede ser modificado
      por los dispositivos de e/S puede, a veces, legítimamente ser volátiles. Un búfer de anillo
      utiliza un adaptador de red, donde adaptador de cambios punteros a
      indican que los descriptores han sido procesados, es un ejemplo de este
      tipo de situación.

    Para la mayoría de código, ninguna de las anteriores justificaciones para volátil aplicar.
    Como resultado, el uso de volátiles es probable que sea visto como un error y
    va a traer más de escrutinio para el código. Los desarrolladores que están
    la tentación de utilizar volátiles deben dar un paso atrás y pensar en lo que
    de verdad están tratando de lograr.

    • «La adición de la palabra clave volatile a asm, que las declaraciones de prevenir esta eliminación.» De verdad?
    • Sí. Véase también gcc.gnu.org/onlinedocs/gcc-4.0.4/gcc/Extended-Asm.html .
    • El spin_lock() se ve como una llamada de función. ¿Qué tiene de especial es que el compilador se tratan de forma especial para que el código generado se «olvide» de cualquier valor de shared_data que se ha leído antes de la spin_lock() y se almacena en un registro, de modo que el valor tiene que ser leído de nuevo en el do_something_on() después de la spin_lock()?
    • Estoy bastante seguro de que cualquier introducción a los cierres/mutexes/etc explicaría esto a usted. Pero basta con decir que las palabras clave en el texto: barrera de memoria. Básicamente, las funciones que necesita para contener algunos específicos de la plataforma de código de operación u otro elemento, que el compilador sabe que es «absolutamente no se puede mover cualquier operación entre esta y la próxima barrera de memoria fuera de la región delimitada por los dos’. Luego, una vez dentro de las barreras, el mutex no asegura ningún otro hilo puede acceder a los datos y las operaciones en shared_data se producen en su orden indicado como el Estándar define que tienen que.
    • Mi punto es que yo no puedo decir el nombre de la función spin_lock() que se hace algo especial. No sé lo que hay en ella. Particularmente, no sé lo que está en la aplicación que impide que el compilador de optimización de lejos las lecturas posteriores.
    • Sincopado tiene un buen punto. Esto significa esencialmente que el programador debe saber la implementación interna de las «funciones especiales» o, al menos, estar muy bien informado acerca de su comportamiento. Esto plantea preguntas adicionales, tales como son estas funciones especiales normalizado y garantizado para trabajar de la misma manera en todas las arquitecturas y todos los compiladores? Hay una lista de funciones disponibles o, al menos, existe un convenio para el uso de comentarios de código para la señal de desarrolladores que la función en cuestión protege el código en contra de ser «optimizado lejos»?
    • No es que el compilador ha de implementar un tratamiento especial, es que el compilador genere código que funciona correctamente con void spin_lock() { shared_data = 7; } y la única manera de hacer eso es la basura de la copia conservada en un registro y leer el valor de la memoria. De hecho, el compilador también tiene que derrame que registrar de nuevo a la memoria la primera, así que void spin_lock() { if (shared_data == 42) abort(); } ve el valor. Así que usted puede ver, tratándolo como una llamada a la función en realidad no es exactamente lo que en el compilador.
    • Supongamos shared_data es un private static. El compilador sabe ahora que spin_lock puede tocarlo. Así que no hay barrera de memoria. ¿Y qué pasa si C99 restringir es añadido?
    • Un private static puede ser tocado por ningún código, a través de un puntero. Y su dirección es tomada. Tal vez el análisis del flujo de datos es capaz de probar que el puntero que nunca se escapa, pero que es en general un problema muy difícil, signos diacríticos en el tamaño del programa. Si usted tiene una forma de garantizar que no se alias existen, a continuación, mueva el acceso a través de un giro de bloqueo en realidad debería estar bien. Pero si no los alias de existir, volatile tiene sentido así. En todos los casos, la «llamada a una función cuyo cuerpo no se ve», el comportamiento será correcta.
    • De acuerdo a este drdobbs.com/cpp/volatile-the-multithreaded-programmers-b/…, está bien para el compilador para optimizar el acceso de cualquier variable en la sección crítica protegidos por la mutua o de la cerradura, ya que dicha sección crítica se serializa y que puede ser considerado como un único hilo de contexto.
    • Que el artículo reconoce el concepto de que un objeto no debe ser accedidos usando un volatile calificador, a veces, cuando es custodiada por un mutex. Para una independiente de la aplicación, el tipo más simple de exclusión mutua construcción es un paso de testigo indicador que dice que es dueño de un grupo de objetos, y cada uno de los contextos usa la bandera para pasar el token en cualquier momento que se quiere que el otro contexto para utilizar el objeto. En una aplicación que se abstiene de mover el resto de las operaciones más allá de una volátil de escritura o delante de un volátil, de leer, de una sola volátiles indicador puede tomar el cuidado de eso.

  3. 11

    No creo que te equivocas — volátil es necesario para garantizar que Un hilo se ve el valor de cambio, si el valor es cambiado por algo distinto de hilo A. Como yo lo entiendo, la volatilidad es básicamente una manera de decirle al compilador de «no almacenar en caché de esta variable en un registro, en lugar asegúrese siempre de lectura/escritura de la memoria RAM, en cada acceso».

    La confusión es porque volátil no es suficiente para la implementación de una serie de cosas. En particular, los sistemas modernos utilizan varios niveles de almacenamiento en caché, moderno CPUs multi-core hacer algún capricho optimizaciones en tiempo de ejecución, y los compiladores modernos, hacer algún capricho optimizaciones en tiempo de compilación, y todos estos pueden causar varios efectos secundarios aparecen en un orden distinto al que cabría esperar si usted acaba de mirar el código fuente.

    Tan volátiles que está bien, siempre y cuando tengan en cuenta que el ‘observado’ los cambios en la volatilidad de la variable no puede producirse en el momento exacto en que usted piensa. Específicamente, no intente utilizar variables volátiles como una manera de sincronizar o el orden de las operaciones a través de los subprocesos, porque no va a funcionar de forma fiable.

    Personalmente, mi principal (¿único?) el uso de los volátiles de la bandera es como un «pleaseGoAwayNow» boolean. Si tengo un subproceso de trabajo que se repite continuamente, voy a tener que comprobar el volátiles booleano en cada iteración del bucle, y la salida si el booleana es verdadera. El hilo principal, a continuación, puede limpiar de forma segura el subproceso de trabajo mediante el establecimiento de la booleanos true y, a continuación, llamar a pthread_join() para esperar hasta que el subproceso de trabajo se ha ido.

    • Su bandera Booleana es probablemente inseguro. ¿Cómo se garantiza que el trabajador finaliza su tarea, y que el indicador permanece en el ámbito de aplicación hasta que se lea (si es que leer)? Que es un trabajo para las señales. Volátil es bueno para la implementación de simple spinlocks si no mutex está involucrado, ya que alias de seguridad significa que el compilador asume mutex_lock (y todos los demás función de biblioteca) puede alterar el estado de la variable de indicador.
    • Obviamente, esto sólo funciona si la naturaleza del subproceso de trabajo de rutina es tal que está garantizado para comprobar el booleano periódicamente. La volatilidad de los bool-bandera se garantiza que se mantienen en su alcance debido al hilo de la secuencia de cierre de siempre se produce antes de que el objeto que contiene la volatilidad de los booleano es destruido, y al hilo de la secuencia de cierre de las llamadas pthread_join() después de configurar el tipo bool. pthread_join() se bloqueará hasta que el subproceso de trabajo se ha ido. Las señales tienen sus propios problemas, especialmente cuando se utilizan en conjunción con subprocesamiento múltiple.
    • Lo siento, me refiero a pthread señales (las variables de condición), no señales POSIX. Todavía no se explican cómo el trabajador está garantizado para completar su trabajo antes de que el Boolean es true. Presumiblemente se debe recibir de las unidades de trabajo en una sección crítica, más trabajo puede ser solicitada después de la bandera. Usted ha descrito una media spinlock, que podría funcionar para usted, pero yo no lo llamaría un patrón de diseño y probablemente no tiene ventajas sobre todo, más seguro, más convencional, mecanismo.
    • El subproceso de no garantizados para completar su trabajo antes de que el boolean es true-de hecho, es casi seguro que estará en el centro de una unidad de trabajo cuando el bool se establece en true. Pero no importa cuando el subproceso de trabajo completa su unidad de trabajo, debido a que el hilo principal no va a hacer nada, a excepción de bloqueo en el interior de pthread_join() hasta que el subproceso de salidas, en cualquier caso. Así que la secuencia de apagado está bien-ordenó — de la volatilidad del tipo bool (y cualquier otros datos compartidos) no será liberado hasta que después de pthread_join() devuelve, y pthread_join() no regresará hasta el subproceso de trabajo se ha ido.
    • estás en lo correcto, en la práctica, pero, teóricamente, todavía podría romper. En un dos sistema básico de un núcleo está constantemente ejecutar el subproceso de trabajo. El otro núcleo se establece el tipo bool true. Sin embargo, no se garantiza el subproceso de trabajo del núcleo verá de que el cambio, es decir, que nunca puede detener a pesar de que repetidas comprueba el tipo bool. Este comportamiento está permitido por el c++0x, java, c# y modelos de memoria. En la práctica esto nunca iba a ocurrir a medida que el ocupado hilo más probable es insertar una barrera de memoria en algún lugar, después de lo cual se verá el cambio en el tipo bool.
    • gracias por la info, es bueno saber. Yo pensaba que la palabra clave volatile trataría con multinúcleo-memoria caché de problemas.
    • un dos sistema básico de un núcleo está constantemente ejecutar el subproceso de trabajo.» Y el sistema operativo nunca hace nada?
    • Usted no puede hacer ninguna suposición sobre el sistema operativo haciendo algo. Usted podría tener un hilo ejecuta en tiempo real sceduling de la política y de alta prioridad y, por tanto, muy pocas veces contexto de conmutación de uno de sus núcleos (piense en un 16 núcleo del sistema que es relativamente hablando, no vert ocupado y su trabajador hilo está haciendo cálculos y nunca/rara vez haciendo llamadas al sistema). OS a veces hacer algo tipo de pensamiento que te lleva a pensar en la muy complicado maneras para asegurarse de que son razonablemente correctos en su caso particular.
    • Quiere decir que un computing hilo con 0 syscalls puede obtener el 100% de tiempo de CPU, sin interrupciones? Es posible en teoría, pero no es muy plausible. Si este es el caso, todo lo que necesitas es enviar una señal asíncrona para el hilo.
    • Tomar un POSIX sistema, utilice el tiempo real de la programación de la política de SCHED_FIFO, mayor prioridad estática de otros procesos/subprocesos del sistema, suficiente núcleos, debería ser perfectamente posible. En Linux se puede especificar que el proceso en tiempo real se puede utilizar el 100% del tiempo de CPU. Ellos nunca cambio de contexto si no hay mayor prioridad del hilo o proceso y nunca bloque I/O. Pero el punto es que C/C++ volatile no está destinado para el cumplimiento adecuado de intercambio de datos/sincronización de la semántica. Me encuentro a la búsqueda de casos para demostrar que el código incorrecto, a veces tal vez podría funcionar es inútil ejercicio.
    • ¿por qué crees que el subproceso de trabajo podría nunca ver el cambio? Es a causa de que el subproceso de no leer la variable o porque el supervisor hilo no escribe la variable? El hilo que necesita una barrera de memoria?

  4. 6

    Su comprensión es realmente malo.

    De la propiedad, que la volatilidad de las variables, es «lee y escribe en esta variable son parte de perceptible el comportamiento del programa». Eso significa que este programa funciona (dado el hardware adecuado):

    int volatile* reg=IO_MAPPED_REGISTER_ADDRESS;
    *reg=1; //turn the fuel on
    *reg=2; //ignition
    *reg=3; //release
    int x=*reg; //fire missiles

    El problema es que esta no es la propiedad que quiero de subprocesos nada.

    Por ejemplo, un hilo de seguridad contador sería justo (linux-kernel-como el código, no sabes la de c++0x equivalente):

    atomic_t counter;
    
    ...
    atomic_inc(&counter);

    Este es atómica, sin una barrera de memoria. Usted debe agregar si es necesario. La adición de volátiles probablemente no ayuda, porque no se refieren al acceso a las cercanas código (por ejemplo. para la adición de un elemento a la lista el contador está contando). Ciertamente, usted no necesita ver el contador se incrementa fuera de su programa, y optimizaciones están siendo deseable, por ejemplo.

    atomic_inc(&counter);
    atomic_inc(&counter);

    todavía puede ser optimizado para

    atomically {
      counter+=2;
    }

    si el optimizador es lo suficientemente inteligente (no cambia la semántica del código).

  5. 6

    volatile es útil (aunque insuficiente) para la implementación de la básico de la construcción de un spinlock mutex, pero una vez que usted tiene que (o algo superior), usted no necesita otra volatile.

    La forma típica de la programación multiproceso no es la protección de cada variable compartida en el nivel de la máquina, pero en lugar de introducir la guardia variables que guía el flujo del programa. En lugar de volatile bool my_shared_flag; usted debe tener

    pthread_mutex_t flag_guard_mutex; //contains something volatile
    bool my_shared_flag;

    Esto no sólo encapsular la «parte dura» es fundamentalmente necesario: C no incluye operaciones atómicas necesarios para implementar un mutex; sólo ha volatile para hacer de extra garantías sobre ordinario operaciones.

    Ahora tienes algo como esto:

    pthread_mutex_lock( &flag_guard_mutex );
    my_local_state = my_shared_flag; //critical section
    pthread_mutex_unlock( &flag_guard_mutex );
    
    pthread_mutex_lock( &flag_guard_mutex ); //may alter my_shared_flag
    my_shared_flag = ! my_shared_flag; //critical section
    pthread_mutex_unlock( &flag_guard_mutex );

    my_shared_flag no necesita ser volátil, a pesar de ser almacenable, porque

    1. Otro hilo tiene acceso a ella.
    2. Significado una referencia a que debe haber sido tomada en algún momento (con la & operador).
      • (O una referencia fue llevado a una que contiene la estructura de la
    3. pthread_mutex_lock es una función de la librería.
    4. Significado que el compilador no puede saber si pthread_mutex_lock de alguna manera adquiere esa referencia.
    5. Significado el compilador debe asumir que pthread_mutex_lock modifes compartido bandera!
    6. Por lo que la variable se debe volver a cargar desde la memoria. volatile, aunque significativo, en este contexto, es extraño.
  6. 6

    Para que sus datos sean consistentes en un entorno concurrente se necesitan dos condiciones para aplicar:

    1) Atomicidad yo.e si yo leer o escribir datos en la memoria que los datos se leen/escriben en una sola pasada y no puede ser interrumpido o sostuvo debido a correo.g un cambio de contexto

    2) Consistencia del yo.e el orden de lectura/escritura de la ops debe ser visto a ser la misma entre los múltiples y concurrentes de los entornos de ser que los hilos, máquinas etc

    volátiles ajusta a ninguno de los anteriores – o, más en particular, el c o el c++ estándar en cuanto a cómo volátiles deben comportarse incluye ninguno de los anteriores.

    Es aún peor, en la práctica, como algunos compiladores ( como el intel Itanium compilador ) hacer el intento de implementar algún elemento de acceso simultáneo comportamiento seguro ( he.e garantizando la memoria vallas ), sin embargo no hay coherencia entre el compilador de implementaciones y, además, la norma no exige este de la implementación en el primer lugar.

    Marcado de una variable como la volatilidad de sólo significa que se está forzando el valor se vacían a y desde la memoria cada vez que en muchos casos sólo ralentiza el código, ya que, básicamente, ha soplado su rendimiento de la caché.

    c# y java AFAIK hacer corregir esta haciendo volátiles se adhieren a 1) y 2) sin embargo no puede decirse lo mismo para c/c++ compiladores así que, básicamente, hacer con él lo que te plazca.

    Para un poco más en profundidad ( aunque no imparcial ) la discusión sobre el tema de lectura este

    • +1 – garantiza la atomicidad era otra pieza de lo que me estaba perdiendo. Yo estaba asumiendo que la carga de un int es atómico, de modo que los volátiles de la prevención de la re-ordenar proporciona la solución completa en la lectura de lado. Creo que es una manera decente de la asunción en la mayoría de arquitecturas, pero no es una garantía.
  7. 5

    La comp.de programación.hilos FAQ un clásico de la explicación por Dave Butenhof:

    Q56: ¿por Qué no es necesario declarar las variables compartidas VOLÁTILES?

    Estoy preocupado, sin embargo, acerca de los casos donde el compilador y el
    librería de hilos de cumplir con sus respectivas especificaciones. Un conformes
    Compilador de C a nivel mundial puede asignar algunos compartida (no volátil) de la variable de
    un registro que se salvan y restauran como la CPU se pasa de
    hilo a hilo. Cada tema tendrá su propio valor para
    esta variable compartida, que no es lo que quieres de un compartida
    variable.

    En cierto sentido esto es cierto, si el compilador sabe lo suficiente acerca de la
    respectivos ámbitos de la variable y el pthread_cond_wait (o
    pthread_mutex_lock) funciones. En la práctica, la mayoría de los compiladores no van a tratar de
    para mantener copias del registro de datos globales a través de una llamada a un externo
    función, porque es muy difícil saber si la rutina podría
    de alguna manera tienes acceso a la dirección de los datos.

    Así que sí, es cierto que un compilador que se ajusta estrictamente (pero muy
    agresiva) de ANSI C no pudo trabajar con múltiples hilos sin
    volátiles. Pero alguien tenía mejor solucionarlo. Debido a que cualquier SISTEMA (es decir,
    de manera pragmática, una combinación de kernel, bibliotecas, y el compilador C) que
    no proporciona el POSIX memoria de coherencia de las garantías no se CUMPLEN
    el estándar POSIX. Período. El sistema NO puede requerir que usted use
    volátil en variables compartidas para el comportamiento correcto, porque POSIX
    sólo requiere que el POSIX funciones de sincronización son necesarios.

    Así que si su programa se rompe porque no la uso volátiles, eso es un ERROR.
    Puede que no sea un error en C, o un error en la librería de hilos, o un error en
    el kernel. Pero es un error del SISTEMA, y uno o más de los componentes
    tendrán que trabajar para solucionarlo.

    Usted no desea utilizar volátiles, debido a que, en cualquier sistema donde se hace
    cualquier diferencia, va a ser mucho más caro que una adecuada
    no volátil variable. (ANSI C requiere de «secuencia de puntos» para los volátiles
    las variables en cada expresión, mientras que la de POSIX requiere únicamente a
    la sincronización de las operaciones, un cálculo intensivo aplicación de subprocesamiento
    verá sustancialmente más memoria de la actividad mediante volátiles, y, después de
    de todo, es la memoria de la actividad que realmente te ralentiza.)

    /—[ Dave Butenhof ]———————–[ [email protected] ]—\

    | Digital Equipment Corporation 110 Spit Brook Rd ZKO2-3/P18 |

    | 603.881.2218, FAX 603.881.0120 Nashua NH 03062-2698 |

    —————–[ Vivir Mejor A Través De La Concurrencia ]—————-/

    Mr Butenhof cubre gran parte de la misma tierra en este usenet post:

    El uso de «volátil», no es suficiente para garantizar una adecuada memoria
    la visibilidad o la sincronización entre hilos. El uso de un mutex es
    suficiente, y, excepto por tener que recurrir a diversas organizaciones no-portátil de la máquina
    código de alternativas, (o más sutiles implicaciones de la memoria POSIX
    reglas que son mucho más difíciles de aplicar en general, como se explica en
    mi post anterior), un mutex es NECESARIO.

    Por lo tanto, como Bryan explicado, el uso de volátiles logra
    nada, pero para evitar que el compilador de decisiones útil y deseable
    optimizaciones, sin ayuda alguna en hacer que el código «hilo
    seguro». Eres bienvenido, por supuesto, para declarar cualquier cosa que desee,
    «volátil», es una legal ANSI C atributo de almacenamiento, después de todo. Sólo
    no es de esperar que para resolver cualquier hilo de problemas de sincronización para usted.

    Todo lo que es igualmente aplicable a la de C++.

    • El enlace está roto, ya no se parece a punto para lo que quería citar. Sin el texto, su tipo de una respuesta sin sentido.
  8. 3

    Todo esto es que «volátil» está haciendo:
    «Hey compilador, esta variable puede cambiar EN CUALQUIER MOMENTO (en cualquier reloj), incluso si NO hay LOCALES de INSTRUCCIONES que actúan sobre él. NO caché de este valor en un registro.»

    Que es. Esto le dice al compilador que su valor es, además, la volatilidad de este valor puede ser modificado en cualquier momento por la lógica externa (otro hilo, otro proceso, el Kernel, etc.). Existe más o menos exclusivamente para suprimir las optimizaciones del compilador que se silenciosamente caché de un valor en un registro que es inherentemente inseguro NUNCA caché.

    Usted puede encontrar artículos como «Dr. Dobbs» que el tono volátil como alguna panacea para la multi-roscado de programación. Su enfoque no está totalmente desprovisto de mérito, pero tiene el defecto fundamental de la fabricación de un objeto de usuarios responsables de su hilo de seguridad, la cual tiende a tener los mismos problemas que las otras violaciones de encapsulación.

  9. 3

    De acuerdo a mi edad estándar de C, «Lo que constituye un acceso a un objeto que tiene la volatilidad de tipo cualificado de la implementación definido». Así que el compilador de C de los escritores podría ha optado por tener «volátil» significa «el hilo de un acceso seguro en un multi-entorno de proceso». Pero ellos no.

    Lugar, las operaciones necesarias para hacer una sección crítica a rosca caja en un multi-core multi-proceso de memoria compartida medio ambiente se han añadido como nueva aplicación definida por características. Y, liberados de la exigencia de que «volátil» daría atómica y acceso pedir en un multi-entorno de proceso, el compilador de escritores de prioridad código de reducción de la más histórica de la implementación depende de los «volátiles» la semántica.

    Esto significa que cosas como «volátil» semáforos críticas secciones del código, que no funcionan en el nuevo hardware con los nuevos compiladores, podría una vez que han trabajado con el viejo de los compiladores en hardware antiguo, y el antiguo ejemplos son, a veces, no es malo, simplemente viejo.

    • El viejo ejemplos necesario que el programa de ser procesados por la calidad de los compiladores que son adecuados para la programación de bajo nivel. Por desgracia, los «modernos», los compiladores han tomado el hecho de que la Norma no requiere de ellos para el proceso de «volátil» de una manera útil como una indicación de que el código que les obligaría a hacerlo está roto, en vez de reconocer que la Norma no hace ningún esfuerzo para prohibir las implementaciones que se están conformando pero de tan baja calidad como para ser de utilidad, pero no en ninguna manera tolera baja calidad -, pero conforme los compiladores que se han hecho populares
    • En la mayoría de plataformas, sería bastante fácil de reconocer lo que volatile tendría que hacer para permitir a uno a escribir un sistema operativo de una manera que es de hardware-dependiente, pero el compilador independiente. Que requiere que los programadores utilizar depende de la implementación de las características en lugar de hacer volatile trabajos que así lo requieran socava el propósito de tener un estándar.

Dejar respuesta

Please enter your comment!
Please enter your name here