2010-07-12 14 views
7

, así que tengo un tipo booleano en C++ en una máquina multiprocesador. La variable comienza la vida como verdadera, y luego hay algunos hilos, uno o más de los cuales podrían escribirlo como falso.¿Debo proteger esta variable con un bloqueo?

Al mismo tiempo, estos hilos también pueden leer esta variable para verificar su estado. No me importa si la lectura de esta variable está sincronizada con cualquiera de las escrituras, cada una sucede en lugares diferentes en el código, y no importa si aparece antes o después de una escritura en particular. Ahora, ¿necesito un candado para este booleano?

La única forma en que necesitaría un bloqueo es que, si el nivel es muy bajo, la memoria se puede corromper con dos escrituras compitiendo. Si, por ejemplo, una instrucción de ensamblaje en el procesador A está escribiendo 0 en el byte que representa el booleano al mismo tiempo que el procesador B está haciendo lo mismo ... y en lugar de haber escrito 0, la memoria termina con el valor 22 o alguna cosa. Eso podría estropear algo.

Entonces, en general, si el proceso A está escribiendo 3 en una ubicación de memoria, mientras que el proceso B está escribiendo 7, sin sincronización, ¿tengo garantizado que terminará al menos con 3 o 7? ¿O es tan fácil romper la memoria?

Editar:

Gracias por los comentarios chicos. Algo más de información: hay sincronización en el programa, por supuesto. Para resumir, la bandera en cuestión dice si un grupo de memoria determinado está "sucio" (necesita ser compactado). Cualquier hilo puede, por lo tanto, decidir establecer este indicador en falso (lo que significa que el grupo está sucio). Por ejemplo, liberar memoria de la agrupación la ensucia. Cualquier hilo también puede leer este indicador y establecer otro indicador para indicar que se necesita una limpieza: esta comprobación se realiza cuando la memoria se asigna desde el grupo, la limpieza se señala si tenemos poca memoria. En algún lugar de mi sección crítica maestra entre iteraciones, donde cada hilo va a buscar más datos para procesar, haré que los hilos comprueben este segundo indicador, y haré algo apropiado para asegurarme de que: todos los otros temas terminen su iteración actual, un hilo limpia la memoria, establece el primer indicador en verdadero (como en el grupo no está sucio), establece el segundo indicador en falso, y luego libera todos los hilos de nuevo.

Así que no creo que necesite un bloqueo porque: un bloqueo garantizaría que una escritura no ocurra al mismo tiempo que otra escritura o lectura. Pero a quién le importa, siempre y cuando el hardware no me falle, el peor de los casos es que la lectura ocurra al azar antes o después de la escritura; esto es lo mismo que sucedería si lo protegiera con un candado, en ese momento tendríamos realmente asegurémonos de que vino antes o después ...

Creo que el mismo argumento se aplica a la segunda bandera que mencioné anteriormente.

+1

Fuera de interés, ¿por qué no te importan las lecturas? Supongamos que algún hilo ha escrito falso, entonces, si no le importa si la lectura da como resultado 'verdadero' o 'falso', ¿por qué molestarse en leer? ¿Es solo que las lecturas "verdaderas" "incorrectas" son inofensivas? –

Respuesta

7

Si solo está comprobando el estado de la variable y configurándola en falso en una dirección, no hay mucho de qué preocuparse, excepto que algunos de los hilos pueden tardar un poco en ver que la variable ya está configurada para falso. (Esto puede superarse en cierto grado mediante el uso de la palabra clave 'volátil'). Luego, dos subprocesos pueden establecerlo en falso, lo que no es problema ya que la variable se establece en un único valor en una dirección. Digamos que la escritura booleana en la ubicación de la memoria no garantiza que sea atómica, ¿cuál es el daño? El valor final que ambos escribirán es el mismo.

Aún así, tendría que utilizar un método de bloqueo si:

  • Valor de ajuste no sólo es de una sola dirección: lo establece en falso, entonces de nuevo a cierto, entonces falsa otra vez, etc.
  • Realiza alguna acción dependiente sobre la información que thread ha establecido el valor en falso. Porque, obviamente, puede haber dos ganadores.
3

Es una manera fácil de romper cosas. Con un booleano, puede estar bien la mayor parte del tiempo, pero no se proporcionan garantías.

Tiene dos opciones: utilizar un mutex (bloqueo) o utilizar primitivas atómicas. Las primitivas atómicas utilizarán instrucciones de hardware para realizar la prueba y establecer las operaciones de forma segura para la ejecución de subprocesos sin requerir un mutex real y son una solución más liviana. El compilador de GNU proporciona acceso a operaciones atómicas a través de extensiones específicas de la arquitectura. También hay bibliotecas de operaciones atómicas portátiles flotando alrededor; la biblioteca C Glib proporciona operaciones atómicas que se basan en el uso de un mutex si las primitivas atómicas no están disponibles, aunque es una biblioteca bastante pesada con muchas otras características también.

Existe la biblioteca Boost.Atomic que abstrae las operaciones atómicas para C++; en función de su nombre, parece que pretende incorporarse a la colección de bibliotecas C++ de Boost, pero aún no lo ha logrado.

1

Para un bool, la respuesta es generalmente no, no se necesita un mutex, pero (como observó Michael E) podría pasar cualquier cosa, por lo que es probable que necesite comprender más acerca de su arco antes de tomar una decisión. Otra nota: el código aún podría necesitar un bloqueo alrededor de la lógica general asociada con el bool, especialmente si el bool se lee más de una vez en el curso de la lógica de una rutina.

Algunos grandes blogs que leo para mantenerme en mis dedos de los pies multiproceso:

http://blogs.msdn.com/b/nativeconcurrency/

http://herbsutter.com/2009/04/20/effective-concurrency-use-thread-pools-correctly-keep-tasks-short-and-nonblocking/

Saludos,

9

En la mayoría del hardware de los productos básicos una sola palabra lecturas/escrituras son atómicas , entonces no, dos (o más) escrituras competidoras (y lecturas) en la misma ubicación de memoria no van a corromper el valor. Lo que es importante aquí es Coherencia de caché entre las CPU.

Una vez más, en hardware, podría bastar con sólo marcar esa única variable booleana volatile (que ha sido declarado inútil para la programación concurrente por cierto) para evitar que el compilador de optimización a cabo en un registro, pero sólo si realmente don No importa la orden de escrituras.

Permítanme reiterar esto con una lista de control:

  • ¿Estás listo hacer perder algunos cambios a que booleana?
  • ¿Seguro hay otras actualizaciones de memoria que vienen antes la tapa booleano en el código fuente, pero puede ser que consiga reordenados después de que el flip van a complicar las cosas?
  • ¿Está seguro de que no le importa el orden de los eventos en su aplicación?

Si tiene tres fuertes respuestas "sí", que podría salirse con no proteger esa bandera. Todavía considere insertar adquirir la barrera de memoria antes de leer la variable y liberar la barrera de memoria antes de escribirla. Sin embargo, mi sugerencia sería volver a pensar en el diseño y diseñar comunicaciones entre hilos sincrónicas claras y secuenciación de eventos.

Espero que esto ayude.

+0

Excelente respuesta, gracias por mencionar la coherencia del caché. –

+0

1) Dices perder actualizaciones de ese booleano ... ¿quieres decir que el hardware ignoraría una escritura? No lo creo, así que no estoy seguro de lo que quieres decir (puedes ver mi apéndice arriba) 2) sí 3) algunas cosas que me importan. nota que nunca leo y escribo esta bandera en el mismo bloque de código. siempre lea una vez o escriba una vez. - Esto es esencialmente por qué la sincronización parece inútil aquí. ¡Creo que encontramos un uso para volátiles! :) – Scott

+0

también es importante tener en cuenta que no estoy modelando un escenario de "espera por ..." en ningún lado aquí. No quiero que un hilo espere a falso, ni esperar nada. ¡Ve! Ve! Ve! – Scott

1

Usted está preguntando acerca de dos cosas.

Primero pregunta por la atomicidad de la asignación de bool. No, no hay garantía de que la asignación booleana sea una operación atómica. En la práctica, generalmente lo es, pero no debes confiar en esto. Algunas arquitecturas extrañas pueden implementar la asignación bool en muchas instrucciones de la máquina ...

Secundario usted pregunta acerca de la corrupción de datos debido a escrituras paralelas: en la práctica, el transporte de la CPU a la memoria se realiza mediante un bus, que casi siempre contiene más bits que tipos primitivos en los que estás trabajando Entonces tal corrupción puede ocurrir para una arquitectura muy extraña o cuando se trabaja en grandes cantidades (no es compatible con el sistema de forma nativa). En la práctica, generalmente obtendrá 3 o 7. Pero nuevamente, no puede confiar en ello.

Para concluir esto, necesita un candado.

1

En la mayoría de hardware básico, las lecturas y escrituras de una sola palabra no son operaciones atómicas; recuerde que tiene máquinas de memoria virtual aquí y cualquiera de esas operaciones puede causar un error de página.

En términos más generales, probablemente le resultará más fácil y más rápido usar mutexes como rutina que rascarse la cabeza preguntándose si se va a escapar sin uno esta vez. Agregar uno donde no es necesario no causa un error; dejando uno donde sea posible.

Cuestiones relacionadas