¿Los tipos fundamentales de C / C++ son atómicos?


Son tipos fundamentales de C / C++, como int, double, etc., atómica, por ejemplo, threadsafe?

¿Están libres de carreras de datos; es decir, si un hilo escribe a un objeto de tal tipo mientras que otro hilo lee de él, está bien definido el comportamiento?

Si no, ¿depende del compilador o de algo más?

Author: Peter Mortensen, 2016-02-05

4 answers

No, tipos de datos fundamentales (p. ej., int, double) no son atómicos, ver std::atomic.

En su lugar puede usar std::atomic<int> o std::atomic<double>.

Nota: std::atomic se introdujo con C++11 y mi entendimiento es que antes de C++11, el estándar de C++ no reconocía la existencia de multihilo en absoluto.


Como señaló @Josh, std::atomic_flag es un tipo atómico booleano. Es garantizado para ser libre de bloqueo , a diferencia de la std::atomic especialidad.


La documentación citada es de: http://open-std.org/JTC1/SC22/WG21/docs/papers/2015/n4567.pdf . Estoy bastante seguro de que el estándar no es gratuito y, por lo tanto, esta no es la versión final/oficial.

1.10 Ejecuciones multiproceso y carreras de datos

  1. Dos evaluaciones de expresiones entran en conflicto si una de ellas modifica una ubicación de memoria (1.7) y la otra lee o modifica la misma ubicación de memoria.
  2. El la biblioteca define un número de operaciones atómicas (Cláusula 29) y operaciones en mutexes (Cláusula 30) que se identifican especialmente como operaciones de sincronización. Estas operaciones desempeñan un papel especial al hacer que las asignaciones en un subproceso sean visibles para otro. Una operación de sincronización en una o más ubicaciones de memoria es una operación de consumo, una operación de adquisición, una operación de liberación o ambas operaciones de adquisición y liberación. Una operación de sincronización sin una ubicación de memoria asociada es una valla y puede ser una valla de adquisición, una valla de liberación, o ambas una valla de adquisición y liberación. Además, hay operaciones atómicas relajadas, que no son operaciones de sincronización, y operaciones atómicas de lectura-modificación-escritura, que tienen características especiales.


  1. Dos acciones son potencialmente concurrentes si
    (23.1) - son realizados por diferentes hilos, o
    (23.2) - no están secuenciados, y al menos uno es realizado por un manejador de señales.
    La ejecución de un programa contiene una carrera de datos si contiene dos acciones potencialmente conflictivas concurrentes, al menos una de las cuales no es atómica, y ninguna sucede antes que la otra, excepto por el caso especial para los manejadores de señales que se describe a continuación. Cualquier carrera de datos de este tipo resulta en un comportamiento indefinido.

29.5 Tipos atómicos

  1. Habrá especializaciones explícitas de la plantilla atómica para la integral tipos " char, signed char, unsigned char, short, unsigned short, int, unsigned int, long, unsigned long, long long, unsigned long long, char16_t, char32_t, wchar_t, y cualquier otro tipo necesario por los typedefs en el encabezado <cstdint>. Para cada tipo integral integral, la especialización atomic<integral> proporciona operaciones atómicas adicionales apropiadas para los tipos integrales. Habrá una especialización atomic<bool> que proporcione las operaciones atómicas generales especificadas en 29.6.1..


  1. habrá especializaciones parciales de puntero de la plantilla de clase atómica. Estas especializaciones tendrán diseño estándar, constructores por defecto triviales y destructores triviales. Cada uno de ellos soportará sintaxis de inicialización agregada.

29.7 Tipo de bandera y operaciones

  1. Las operaciones en un objeto de tipo atomic_flag estarán libres de bloqueos. [Nota: Por lo tanto, las operaciones también deben estar libres de direcciones. Ningún otro tipo requiere operaciones sin bloqueo, por lo que el atomic_flag type es el tipo mínimo implementado por hardware necesario para cumplir con esta norma internacional. Los tipos restantes se pueden emular con atomic_flag, aunque con propiedades menos que ideales. - nota final ]
 68
Author: James Adkison,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2016-02-06 15:52:45

Dado que C también se menciona (actualmente) en la pregunta a pesar de no estar en las etiquetas, el Estándar C establece:

5.1.2.3 Ejecución del programa

...

Cuando el procesamiento de la máquina abstracta se interrumpe por la recepción de una señal, los valores de los objetos que no son los objetos nor de tipo volatile sig_atomic_t no están especificados, al igual que state of the floating-point environment. El valor de cualquier objeto modificado por el manejador que no es ni un objeto atómico libre de bloqueo ni de tipo volatile sig_atomic_t se vuelve indeterminado cuando el controlador salidas, al igual que el estado del entorno de coma flotante si es modificado por el controlador y no restaurado a su estado original.

Y

5.1.2.4 Ejecuciones multiproceso y carreras de datos

...

Dos evaluaciones de expresiones conflicto si uno de ellos modifica una ubicación de memoria y el otro lee o modifica la misma ubicación de memoria.

[varias páginas de estándares - algunos párrafos que abordan explícitamente los tipos atómicos]

La ejecución de un programa contiene una data race si contiene dos acciones en conflicto en subprocesos diferentes, al menos una de las cuales no es atómica, y ninguna de las dos sucede antes que el otro. Cualquier carrera de datos de este tipo resulta en un comportamiento indefinido.

Tenga en cuenta que los valores son "indeterminados" si una señal interrumpe el procesamiento y el acceso simultáneo a tipos que no son explícitamente atómicos es un comportamiento indefinido.

 16
Author: Andrew Henle,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2016-02-05 14:23:37

¿Qué es atómica?

Atómico, como describir algo con la propiedad de un átomo. La palabra átomo proviene del latín atomus que significa "indiviso".

Normalmente pienso que una operación atómica (independientemente del lenguaje) tiene dos cualidades: {[23]]}

Una operación atómica es siempre indivisa.

Es decir, se realiza de manera indivisible, creo que esto es a lo que OP se refiere como "threadsafe". En cierto sentido, la operación ocurre instantáneamente cuando visto por otro hilo.

Por ejemplo, es probable que la siguiente operación esté dividida (dependiente del compilador/hardware):

i += 1;

Porque puede ser observado por otro hilo (en hardware hipotético y compilador) como:

load r1, i;
addi r1, #1;
store i, r1;

Dos subprocesos haciendo la operación anterior i += 1 sin la sincronización adecuada pueden producir un resultado incorrecto. Decir i=0 inicialmente, el hilo T1 carga T1.r1 = 0, y el hilo T2 carga t2.r1 = 0. Ambos hilos incrementan sus respectivos r1s en 1 y luego almacenar el resultado a i. Aunque se han realizado dos incrementos, el valor de i sigue siendo solo 1 porque la operación de incremento era divisible. Tenga en cuenta que si hubiera habido sincronización antes y después de i+=1, el otro subproceso habría esperado hasta que la operación se completara y, por lo tanto, habría observado una operación indivisa.

Tenga en cuenta que incluso una simple escritura puede o no ser indivisa:

i = 3;

store i, #3;

Dependiendo del compilador y hardware. Por ejemplo, si la dirección de i no está alineada adecuadamente, entonces se debe usar una carga/almacén no alineada que es ejecutada por la CPU como varias cargas/almacenes más pequeños.

Una operación atómica tiene garantizada la semántica de ordenamiento de memoria.

Las operaciones no atómicas pueden ser reordenadas y no necesariamente ocurren en el orden escrito en el código fuente del programa.

Por ejemplo, bajo la regla "como si" al compilador se le permite reordenar las tiendas y las cargas como considere conveniente mientras como todo el acceso a la memoria volátil se produce en el orden especificado por el programa "como si" el programa fue evaluado de acuerdo con la redacción en el estándar. Por lo tanto, las operaciones no atómicas pueden reorganizarse rompiendo cualquier suposición sobre el orden de ejecución en un programa multihilo. Esta es la razón por la que un uso aparentemente inocente de un raw int como una variable de señalización en la programación multihilo está roto, incluso si las escrituras y lecturas pueden ser indivisibles, el orden puede romper el programa dependiendo de la compilación. Una operación atómica impone el orden de las operaciones a su alrededor dependiendo de qué semántica de memoria se especifique. Véase std::memory_order.

La CPU también puede reordenar los accesos de memoria bajo las restricciones de ordenación de memoria de esa CPU. Puede encontrar las restricciones de ordenación de memoria para la arquitectura x86 en el Manual para Desarrolladores de Software de Arquitecturas Intel 64 e IA32 sección 8.2 a partir de la página 2212.

Tipos Primitivos (int, char etc) son no atómico

Porque incluso si bajo ciertas condiciones pueden tener instrucciones indivisibles de almacenamiento y carga o posiblemente incluso algunas instrucciones aritméticas, no garantizan el pedido de almacenes y cargas. Como tales, no son seguros de usar en contextos multihilo sin la sincronización adecuada para garantizar que el estado de memoria observado por otros hilos sea lo que crees que es en ese momento.

Espero que esto explique por qué los tipos primitivos no son atómico.

 10
Author: Emily L.,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2016-02-06 23:12:02

Una información adicional que no he visto mencionada en las otras respuestas hasta ahora:

Si usa std::atomic<bool>, por ejemplo, y bool es realmente atómico en la arquitectura de destino, entonces el compilador no generará cercas o bloqueos redundantes. Se generaría el mismo código que para un simple bool.

En otras palabras, usar std::atomic solo hace que el código sea menos eficiente si realmente se requiere para ser correcto en la plataforma. Así que no hay razón para evitarlo.

 3
Author: M.M,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2016-02-05 23:45:42