c++ - teclado - float arduino español
¿Es seguro leer una variable entera que se modifique simultáneamente sin bloqueo? (12)
Depende de tu plataforma. La mayoría de las plataformas modernas ofrecen operaciones atómicas para enteros: Windows tiene InterlockedIncrement, InterlockedDecrement, InterlockedCompareExchange, etc. Estas operaciones suelen ser compatibles con el hardware subyacente (léase: la CPU) y suelen ser más económicas que utilizar una sección crítica u otros mecanismos de sincronización.
Ver MSDN: InterlockedCompareExchange
Creo que Linux (y las variantes modernas de Unix) admiten operaciones similares en el paquete pthreads, pero no pretendo ser un experto allí.
Supongamos que tengo una variable entera en una clase, y esta variable puede ser modificada simultáneamente por otros hilos. Las escrituras están protegidas por un mutex. ¿Debo proteger también las lecturas? He oído que hay algunas arquitecturas de hardware en las que, si un hilo modifica una variable y otro hilo lo lee, el resultado de la lectura será basura; en este caso, necesito proteger las lecturas. Aunque nunca había visto tales arquitecturas.
Esta pregunta supone que una sola transacción solo consiste en actualizar una única variable entera, por lo que no me preocupan los estados de otras variables que también podrían estar involucradas en una transacción.
En general, cada instrucción de máquina pasa por varias etapas de hardware cuando se ejecuta. Como la mayoría de las CPU actuales son multinúcleo o hiperendenadas, eso significa que leer una variable puede comenzar a moverse a través de la interconexión de instrucciones, pero no detiene la ejecución simultánea de una instrucción de tienda en otro núcleo o subproceso de la CPU. dirección. Las dos instrucciones simultáneas de ejecución, lectura y almacenamiento, podrían "cruzarse", lo que significa que la lectura recibirá el valor anterior justo antes de que se almacene el nuevo valor.
Para reanudar: necesita el mutex para lectura y escritura.
Esto puede ocurrir en sistemas de 8 bits que usan números enteros de 16 bits.
Si desea evitar el bloqueo, en las circunstancias adecuadas puede salirse con la suya varias veces, hasta que obtenga dos valores iguales consecutivos. Por ejemplo, he usado este enfoque para leer el reloj de 64 bits en un objetivo incrustado de 32 bits, donde el tic del reloj se implementó como una rutina de interrupción. En ese caso, leer tres veces es suficiente, porque el reloj solo puede marcar una vez en el poco tiempo que se ejecuta la rutina de lectura.
Hace una pregunta sobre la lectura de una variable y luego habla de actualizar una variable, lo que implica una operación de lectura, modificación y escritura.
Suponiendo que realmente se refiera a lo primero, la lectura es segura si se trata de una operación atómica . Para casi todas las arquitecturas esto es cierto para enteros.
Hay algunas excepciones (y raras):
- La lectura está desalineada, por ejemplo, accediendo a un int de 4 bytes en una dirección impar. Por lo general, debe forzar al compilador con atributos especiales para que realice una desalineación.
- El tamaño de un int es más grande que el tamaño natural de las instrucciones, por ejemplo, usando 16 bit ints en una arquitectura de 8 bits.
- Algunas arquitecturas tienen un ancho de bus artificialmente limitado. Solo sé de los muy viejos y obsoletos, como un 386sx o un 68008.
Imagine que está leyendo la variable en un hilo, ese hilo se interrumpe durante la lectura y la variable se cambia por un hilo de escritura. Ahora, ¿cuál es el valor del entero leído después de que se reanude el hilo de lectura?
A menos que leer una variable sea una operación atómica, en este caso solo se necesita una instrucción individual (de ensamblaje), no se puede garantizar que la situación anterior no pueda suceder. (La variable se podría escribir en la memoria, y recuperar el valor tomaría más de una instrucción)
El consenso es que debe encapsular / bloquear todas las escrituras individualmente, mientras que las lecturas se pueden ejecutar simultáneamente con (solo) otras lecturas
Si bien es probable que sea seguro leer ints en sistemas de 32 bits sin sincronización. No me arriesgaría. Si bien las lecturas concurrentes múltiples no son un problema, no me gusta que las escrituras ocurran al mismo tiempo que las lecturas.
Yo recomendaría colocar las lecturas en la sección crítica también y luego someter a prueba su aplicación en múltiples núcleos para ver si esto está causando demasiada controversia. Encontrar errores de concurrencia es una pesadilla que prefiero evitar. ¿Qué sucede si en el futuro alguien decide cambiar el int a un long long o double, para que puedan contener números más grandes?
Si tienes una buena biblioteca de hilos como boost.thread o zthread, entonces deberías tener bloqueos de lectura / escritura. Estos serían ideales para su situación, ya que permiten múltiples lecturas mientras protegen las escrituras.
Si no usa el valor prevous de esta variable cuando escribe nuevo, entonces:
Puede leer y escribir variable entera sin usar mutex. Se debe a que el entero es de tipo base en la arquitectura de 32 bits y cada modificación / lectura del valor se realiza con una sola operación.
Pero, si usted dona algo como incremento:
myvar++;
Entonces necesita usar mutex, porque esta construcción se expande a myvar = myvar + 1 y entre read myvar e incremente myvar, myvar puede modificarse. En ese caso, obtendrá un mal valor.
Tanto la lectura como la escritura de variables con concurrencia deben estar protegidas por una sección crítica (no mutex). A menos que quieras perder tu depuración todo el día.
Las secciones críticas son específicas de la plataforma, creo. En Win32, la sección crítica es muy eficiente: cuando no hay enclavamiento, ingresar la sección crítica es casi gratuito y no afecta el rendimiento general. Cuando se produce el enclavamiento, es aún más eficiente que mutex, porque implementa una serie de comprobaciones antes de suspender el hilo.
Recomiendo no confiar en ningún compilador o arquitectura en este caso.
Siempre que tenga una mezcla de lectores y escritores (en lugar de simplemente lectores o simplemente escritores), será mejor que los sincronice a todos. Imagine que su código ejecuta un corazón artificial de alguien, realmente no desea que lea valores erróneos, y seguramente no quiere que una planta de energía en su ciudad sea ''boooom'' porque alguien decidió no usar ese mutex. Ahórrate una noche de sueño a largo plazo, sincronízalos.
Si solo tiene una lectura de hilo, puede usar solo ese mutex, sin embargo, si planea múltiples lectores y varios escritores, necesitará un código sofisticado para sincronizarlo. Aún no he visto una buena implementación de bloqueo de lectura / escritura que también sea "justa".
lectura atómica
Como dije antes, depende de la plataforma. En x86, el valor debe estar alineado en un límite de 4 bytes. En general, para la mayoría de las plataformas, la lectura debe ejecutarse en una única instrucción de CPU.
almacenamiento en caché del optimizador
El optimizador no sabe que está leyendo un valor modificado por un hilo diferente. declarar el valor volatile
ayuda con eso: el optimizador emitirá una lectura / escritura de memoria para cada acceso, en lugar de tratar de mantener el valor en caché en un registro.
Caché de la CPU
Aún así, puede leer un valor obsoleto, ya que en las arquitecturas modernas tiene múltiples núcleos con caché individual que no se mantiene sincronizado automáticamente. Necesita una barrera de lectura de memoria, generalmente una instrucción específica de plataforma.
En Wintel, las funciones de sincronización de subprocesos agregarán automáticamente una barrera de memoria completa, o puede usar las funciones InterlockedXxxx .
MSDN: problemas de memoria y sincronización , macro MemoryBarrier
[edit] por favor también vea los comentarios de Drhirsch.
Supongamos que tengo una variable entera en una clase, y esta variable puede ser modificada simultáneamente por otros hilos. Las escrituras están protegidas por un mutex. ¿Debo proteger también las lecturas? He oído que hay algunas arquitecturas de hardware en las que, si un hilo modifica una variable y otro hilo lo lee, el resultado de la lectura será basura; en este caso, necesito proteger las lecturas. Aunque nunca había visto tales arquitecturas.
En el caso general, eso es potencialmente cada arquitectura. Cada arquitectura tiene casos en los que la lectura concurrente con una escritura dará como resultado la basura. Sin embargo, casi todas las arquitecturas también tienen excepciones a esta regla.
Es común que las variables de tamaño de palabra se lean y escriban de manera atómica, por lo que no es necesario sincronizarlas cuando lee o escribe. El valor correcto se escribirá atómicamente como una operación única, y los hilos leerán el valor actual como una única operación atómica, incluso si está escribiendo otro hilo. Entonces, para enteros, estás a salvo en la mayoría de las arquitecturas. Algunos extenderán esta garantía a algunos otros tamaños también, pero eso obviamente depende del hardware.
Para las variables que no son del tamaño de una palabra, tanto la lectura como la escritura serán típicamente no atómicas y deberán sincronizarse por otros medios.
Si una variable está marcada con la palabra clave volátil, la lectura / escritura se convierte en atómica, pero esto tiene muchas, muchas otras implicaciones en términos de lo que hace el compilador y cómo se comporta, y no solo debe usarse para este propósito.
Lea lo que es volátil antes de comenzar a usarlo ciegamente: http://msdn.microsoft.com/en-us/library/12a04hfd(VS.80).aspx