Pregunta

En la mayoría de las plataformas comunes (el más importante es x86; entiendo que algunas plataformas tienen modelos de memoria extremadamente difíciles que casi no ofrecen garantías útiles para el multihilo, pero no me interesan los ejemplos de contadores raros), es el siguiente código seguro?

Tema 1:

someVariable = doStuff();
atomicSet(stuffDoneFlag, 1);

Tema 2:

while(!atomicRead(stuffDoneFlag)) {}  // Wait for stuffDoneFlag to be set.
doMoreStuff(someVariable);

Suponiendo implementaciones estándar y razonables de operaciones atómicas:

  1. ¿Se garantiza que la asignación de Thread 1 a someVariable se complete antes de que se llame a atomicSet () ?
  2. ¿Se garantiza que el subproceso 2 vea la asignación de someVariable antes de llamar a doMoreStuff () siempre que lea stuffDoneFlag atómicamente?

Ediciones:

  1. La implementación de operaciones atómicas que estoy usando contiene la instrucción <86> BLOQUEO x86 en cada operación, si eso ayuda.
  2. Supongamos que stuffDoneFlag se borra correctamente de alguna manera. Cómo no es importante.
  3. Este es un ejemplo muy simplificado. Lo creé de esta manera para que no tuvieras que entender todo el contexto del problema para responderlo. Sé que no es eficiente.
¿Fue útil?

Solución

Si su código x86 real tiene el almacén en alguna Variable antes de la tienda en atomicSet en el Subproceso 1 y carga de alguna Variable después de la carga en atomicRead en el Subproceso 2, entonces debería estar bien. Volumen 3A del Manual del desarrollador de software de Intel especifica el modelo de memoria para x86 en la Sección 8.2, y Las restricciones de almacenamiento y carga dentro del subproceso deberían ser suficientes aquí.

Sin embargo, es posible que no haya nada que impida que el compilador reordene las instrucciones generadas desde cualquier lenguaje de nivel superior que esté utilizando en las operaciones atómicas.

Otros consejos

1) Sí

2) Sí

Ambos trabajan.

Este código parece seguro, pero cuestiono la eficacia de su spinlock (mientras bucle) a menos que solo esté girando por un período de tiempo muy corto. No hay garantía en ningún sistema dado de que Thread 2 no acapare por completo todo el tiempo de procesamiento.

Recomendaría usar algunas primitivas de sincronización reales (se parece a boost :: condition_variable es lo que desea aquí) en lugar de confiar en el bloqueo de giro.

Las instrucciones atómicas aseguran que el hilo 2 espera a que el hilo 1 complete la configuración de la variable antes de que el hilo 2 continúe. Hay, sin embargo, dos cuestiones clave:

1) el someVariable debe ser declarado ' volatile 'para garantizar que el compilador no optimice su asignación, por ejemplo almacenándolo en un registro o aplazando una escritura.

2) el segundo hilo está bloqueando mientras espera la señal (denominada spinlocking ). Su plataforma probablemente proporciona mecanismos y mecanismos de bloqueo y señalización mucho mejores, pero una mejora relativamente sencilla sería simplemente sleep () en el while () del hilo 2 ( body.) p>

dsimcha escrito: " Supongamos que stuffDoneFlag está correctamente borrado de alguna manera. ¿Cómo no es importante? & Quot; ¡Esto no es cierto!

Veamos el escenario:

  1. Thread2 comprueba el stuffDoneFlag si es 1 empezar a leer someVariable.
  2. Antes de que Thread2 termine de leer, el programador de tareas interrumpe su tarea y suspende la tarea por algún tiempo.
  3. Thread1 vuelve a acceder a someVariable y cambia el contenido de la memoria.
  4. El programador de tareas enciende nuevamente Thread2 y continúa el trabajo, ¡pero el contenido de la memoria de alguna Variable cambia!
Licenciado bajo: CC-BY-SA con atribución
No afiliado a StackOverflow
scroll top