Pregunta

Tengo una aplicación donde se ejecutan 2 hilos...¿Existe alguna certeza de que cuando cambio una variable global de un hilo, el otro notará este cambio?No tengo ningún sistema de sincronización o exclusión mutua implementado...pero si este código funciona todo el tiempo (imagine un global booleano llamado datosActualizado):

Hilo 1:

while(1) {
    if (dataUpdated)
        updateScreen();
    doSomethingElse();
}

Hilo 2:

while(1) {
    if (doSomething())
        dataUpdated = TRUE;
}

¿Un compilador como gcc optimiza este código de una manera que no verifica el valor global, solo considerando su valor en el momento de la compilación (porque nunca se cambia en el mismo hilo)?

PD:Al ser esto para una aplicación tipo juego, realmente no importa si habrá una lectura mientras se escribe el valor...Lo único que importa es que el otro hilo note el cambio.

¿Fue útil?

Solución

Sí.No.Tal vez.

Primero, como otros han mencionado, es necesario hacer que dataUpdated sea volátil;de lo contrario, el compilador puede tener libertad para sacar la lectura del bucle (dependiendo de si puede ver o no que doSomethingElse no lo toca).

En segundo lugar, dependiendo de su procesador y de sus necesidades de pedido, es posible que necesite barreras de memoria.volátil es suficiente para garantizar que el otro procesador verá el cambio eventualmente, pero no lo suficiente para garantizar que los cambios se verán en el orden en que se realizaron.Su ejemplo solo tiene una bandera, por lo que realmente no muestra este fenómeno.Si necesita y utiliza barreras de memoria, ya no debería necesitar volátil

Volátil considerado dañino y Barreras de memoria del kernel de Linux son buenos antecedentes sobre las cuestiones subyacentes;Realmente no conozco nada similar escrito específicamente para subprocesos.Afortunadamente, los hilos no plantean estas preocupaciones con tanta frecuencia como lo hacen los periféricos de hardware, aunque el tipo de caso que usted describe (un indicador que indica finalización, con otros datos que se presumen válidos si el indicador está activado) es exactamente el tipo de cosas en las que realizar un pedido importa...

Otros consejos

A continuación se muestra un ejemplo que utiliza variables de condición de impulso:

bool _updated=false;
boost::mutex _access;
boost::condition _condition;

bool updated()
{
  return _updated;
}

void thread1()
{
  boost::mutex::scoped_lock lock(_access);
  while (true)
  {
    boost::xtime xt;
    boost::xtime_get(&xt, boost::TIME_UTC);
    // note that the second parameter to timed_wait is a predicate function that is called - not the address of a variable to check
    if (_condition.timed_wait(lock, &updated, xt))
      updateScreen();
    doSomethingElse();
  }
}

void thread2()
{
  while(true)
  {
    if (doSomething())
      _updated=true;
  }
}

Utilice un candado.Utilice siempre un candado para acceder a los datos compartidos.Marcar la variable como volátil evitará que el compilador optimice la memoria leída, pero no evitará otros problemas como reordenamiento de la memoria.Sin un bloqueo, no hay garantía de que las escrituras en la memoria en doSomething() sean visibles en la función updateScreen().

La única otra forma segura es utilizar un valla de memoria, ya sea explícita o implícitamente usando una función Interlocked*, por ejemplo.

Utilizar el volátil palabra clave para indicarle al compilador que el valor puede cambiar en cualquier momento.

volatile int myInteger;

Lo anterior garantizará que cualquier acceso a la variable será hacia y desde la memoria sin ninguna optimización específica y, como resultado, todos los subprocesos que se ejecutan en el mismo procesador "verán" cambios en la variable con la misma semántica que lee el código.

Chris Jester-Young señaló que pueden surgir problemas de coherencia ante un cambio de valor tan variable en sistemas multiprocesador.Esta es una consideración y depende de la plataforma.

En realidad, hay dos consideraciones a considerar en relación con la plataforma.Son coherencia y atomicidad de las transacciones de la memoria.

En realidad, la atomicidad es una consideración para plataformas de un solo procesador y de múltiples procesadores.El problema surge porque es probable que la variable sea de naturaleza multibyte y la pregunta es si un hilo podría ver una actualización parcial del valor o no.es decir:Algunos bytes cambiaron, cambio de contexto, valor no válido leído al interrumpir el hilo.Para una sola variable que tenga el tamaño de palabra natural de la máquina o más pequeña y esté alineada naturalmente, no debería ser una preocupación.Específicamente, un En t El tipo siempre debería estar bien en este sentido siempre que esté alineado, que debería ser el caso predeterminado para el compilador.

En relación con la coherencia, esta es una preocupación potencial en un sistema multiprocesador.La pregunta es si el sistema implementa coherencia de caché total o no entre procesadores.Si se implementa, esto normalmente se hace con el protocolo MESI en el hardware.La pregunta no indicaba plataformas, pero tanto las plataformas Intel x86 como las plataformas PowerPC tienen caché coherente en todos los procesadores para regiones de datos de programas normalmente asignadas.Por lo tanto, este tipo de problema no debería ser una preocupación para los accesos ordinarios a la memoria de datos entre subprocesos, incluso si hay varios procesadores.

La última cuestión relativa a la atomicidad que surge es específica de la atomicidad de lectura-modificación-escritura.Es decir, ¿cómo se garantiza que si un valor se lee, se actualiza en valor y se escribe, esto suceda de forma atómica, incluso entre procesadores si hay más de uno?Entonces, para que esto funcione sin objetos de sincronización específicos, sería necesario que todos los subprocesos potenciales que accedan a la variable sean SÓLO lectores, pero se espera que solo un subproceso pueda ser escritor a la vez.Si este no es el caso, entonces necesita un objeto de sincronización disponible para poder garantizar acciones atómicas en acciones de lectura, modificación y escritura en la variable.

Su solución utilizará 100% CPU, entre otros problemas.Busque en Google "variable de condición".

Chris Jester-Young señaló que:

Esto solo funciona bajo el modelo de memoria de Java 1.5+.El estándar C++ no aborda los subprocesos y volátil no garantiza la coherencia de la memoria entre procesadores.Necesitas una barrera de memoria para esto.

Siendo así, la única respuesta verdadera es implementar un sistema de sincronización, ¿verdad?

Utilizar el volátil palabra clave para indicarle al compilador que el valor puede cambiar en cualquier momento.

volatile int myInteger;

No, no es seguro.Si declara la variable volátil, se supone que el compilador generará código que siempre carga la variable desde la memoria en una lectura.

Si el alcance es correcto ("externo", global, etc.) entonces se notará el cambio.La pregunta es ¿cuándo?¿Y en qué orden?

El problema es que el compilador poder y frecuentemente voluntad Reordene su lógica para llenar todas sus canalizaciones simultáneas como optimización del rendimiento.

Realmente no se muestra en su ejemplo específico porque no hay otras instrucciones relacionadas con su tarea, pero imagine funciones declaradas después de que se ejecute su asignación bool. antes la asignación.

Verificar Peligro de tubería en wikipedia o busque en google "reordenamiento de instrucciones del compilador"

Como otros han dicho el volatile La palabra clave es tu amiga.:-)

Lo más probable es que descubras que tu código funcionaría cuando tuvieras todas las opciones de optimización deshabilitadas en gcc.En este caso (creo) trata todo como volátil y, como resultado, se accede a la variable en la memoria para cada operación.

Con cualquier tipo de optimización activada, el compilador intentará utilizar una copia local mantenida en un registro.Dependiendo de sus funciones, esto puede significar que solo vea el cambio de variable de forma intermitente o, en el peor de los casos, nunca.

Usando la palabra clave volatile indica al compilador que el contenido de esta variable puede cambiar en cualquier momento y que debe no utilice una copia almacenada en caché local.

Dicho todo esto, es posible que encuentre mejores resultados (como lo menciona jeff) mediante el uso de un semáforo o variable de condición.

Este Es una introducción razonable al tema.

Licenciado bajo: CC-BY-SA con atribución
No afiliado a StackOverflow
scroll top