¿Son las lecturas y escrituras de C ++ de un int Atomic?

81

Tengo dos hilos, uno actualizando un int y otro leyéndolo. Este es un valor estadístico en el que el orden de las lecturas y escrituras es irrelevante.

Mi pregunta es, ¿necesito sincronizar el acceso a este valor multibyte de todos modos? O, dicho de otra manera, puede que parte de la escritura se complete y se interrumpa, y luego suceda la lectura.

Por ejemplo, piense en un valor = 0x0000FFFF que obtiene un valor incrementado de 0x00010000.

¿Hay algún momento en el que el valor parezca 0x0001FFFF por el que debería estar preocupado? Ciertamente, cuanto más grande sea el tipo, más posible que suceda algo como esto.

Siempre he sincronizado este tipo de accesos, pero tenía curiosidad por saber qué piensa la comunidad.

theschmitzer
fuente
5
De Verdad? No me importaría lo que pensara la comunidad. Me importa cuáles sean los hechos :)
sehe
1
Lectura
ereOn
Específicamente para =: stackoverflow.com/questions/8290768/…
Ciro Santilli 郝海东 冠状 病 六四 事件 法轮功

Respuestas:

47

Al principio, uno podría pensar que las lecturas y escrituras del tamaño de la máquina nativa son atómicas, pero hay una serie de problemas que abordar, incluida la coherencia de la caché entre procesadores / núcleos. Utilice operaciones atómicas como Interlocked * en Windows y el equivalente en Linux. C ++ 0x tendrá una plantilla "atómica" para envolverlos en una interfaz agradable y multiplataforma. Por ahora, si está utilizando una capa de abstracción de plataforma, puede proporcionar estas funciones. ACE lo hace, consulte la plantilla de clase ACE_Atomic_Op .

Adam Mitz
fuente
El documento de ACE_Atomic_Op se ha movido; ahora se puede encontrar en dre.vanderbilt.edu/~schmidt/DOC_ROOT/ACE/ace/Atomic_Op.inl
Byron
63

Chico, que pregunta. La respuesta a cuál es:

Si, no, hmmm, bueno, depende

Todo se reduce a la arquitectura del sistema. En un IA32, una dirección correctamente alineada será una operación atómica. Las escrituras no alineadas pueden ser atómicas, depende del sistema de almacenamiento en caché en uso. Si la memoria se encuentra dentro de una sola línea de caché L1, entonces es atómica; de lo contrario, no lo es. El ancho del bus entre la CPU y la RAM puede afectar la naturaleza atómica: una escritura de 16 bits correctamente alineada en un 8086 era atómica, mientras que la misma escritura en un 8088 no lo era porque el 8088 solo tenía un bus de 8 bits mientras que el 8086 tenía un Bus de 16 bits.

Además, si está utilizando C / C ++, no olvide marcar el valor compartido como volátil, de lo contrario, el optimizador pensará que la variable nunca se actualiza en uno de sus hilos.

Skizz
fuente
23
La palabra clave volátil no es útil en programas multiproceso stackoverflow.com/questions/2484980/…
5
@IngeHenriksen: No me convence ese enlace.
Skizz
otra fuente, pero desafortunadamente muy antigua (es anterior a std :: atomic): web.archive.org/web/20190219170904/https://software.intel.com/…
Max Barraclough
11

SI está leyendo / escribiendo un valor de 4 bytes Y está alineado con DWORD en la memoria Y está ejecutando en la arquitectura I32, ENTONCES las lecturas y escrituras son atómicas.

Gabriel
fuente
2
¿En qué parte de los manuales para desarrolladores de software de arquitectura Intel se indica esto?
Daniel Trebbien
2
@DanielTrebbien: tal vez vea stackoverflow.com/questions/5002046/…
sehe
9

Sí, necesitas sincronizar accesos. En C ++ 0x será una carrera de datos y un comportamiento indefinido. Con los hilos POSIX, su comportamiento ya no está definido.

En la práctica, es posible que obtenga valores incorrectos si el tipo de datos es mayor que el tamaño de la palabra nativa. Además, es posible que otro hilo nunca vea el valor escrito debido a las optimizaciones que mueven la lectura y / o escritura.

Anthony Williams
fuente
3

Debe sincronizar, pero en ciertas arquitecturas hay formas eficientes de hacerlo.

Lo mejor es usar subrutinas (quizás enmascaradas detrás de macros) para que pueda reemplazar condicionalmente las implementaciones con las específicas de la plataforma.

El kernel de Linux ya tiene parte de este código.

Jason Cohen
fuente
3

En Windows, se garantiza que Interlocked *** Exchange *** Add será atómico.

Andrew Stein
fuente
2

Para hacer eco de lo que todos dijeron en el piso de arriba, el lenguaje anterior a C ++ 0x no puede garantizar nada sobre el acceso a la memoria compartida desde múltiples subprocesos. Cualquier garantía dependería del compilador.

Tim Cooper
fuente
0

No, no lo son (o al menos no puedes asumir que lo son). Habiendo dicho eso, hay algunos trucos para hacer esto de forma atómica, pero normalmente no son portátiles (consulte Comparar e intercambiar ).

Leon Timmermans
fuente
0

Estoy de acuerdo con muchos y especialmente con Jason . En Windows, probablemente se usaría InterlockedAdd y sus amigos.

Kenny
fuente
0

Aparte del problema de caché mencionado anteriormente ...

Si transfiere el código a un procesador con un tamaño de registro más pequeño, ya no será atómico.

En mi opinión, los problemas de enhebrado son demasiado espinosos como para arriesgarse.

JeffV
fuente
0

La única forma portátil es usar el tipo sig_atomic_t definido en el encabezado signal.h para su compilador. En la mayoría de las implementaciones de C y C ++, es un int. Luego declare su variable como "sig_atomic_t volátil".

Edward Howorka
fuente
volatile no hace lo que crees que hace stackoverflow.com/questions/2484980/…
Sam Miller
0

Tomemos este ejemplo

int x;
x++;
x=x+5;

Se supone que la primera declaración es atómica porque se traduce en una sola directiva de ensamblado INC que toma un solo ciclo de CPU. Sin embargo, la segunda asignación requiere varias operaciones, por lo que claramente no es una operación atómica.

Otro por ejemplo,

x=5;

Nuevamente, debe desensamblar el código para ver qué sucede exactamente aquí.

siddhusingh
fuente
2
Pero el compilador podría optimizarlo en x+=6.
tc.
0

tc, creo que en el momento en que use una constante (como 6), la instrucción no se completará en un ciclo de máquina. Intente ver el conjunto de instrucciones de x + = 6 en comparación con x ++

siddhusingh
fuente
0

Algunas personas piensan que ++ c es atómico, pero están atentos al ensamblado generado. Por ejemplo con 'gcc -S':

movl    cpt.1586(%rip), %eax
addl    $1, %eax
movl    %eax, cpt.1586(%rip)

Para incrementar un int, el compilador primero lo carga en un registro y lo almacena nuevamente en la memoria. Esto no es atómico.

Etham
fuente
1
Esto no es un problema si solo un hilo está escribiendo en la variable, ya que no hay rasgaduras.
Ben Voigt
0

¡Definitivamente NO! Esa respuesta de nuestra máxima autoridad en C ++, M. Boost:
No se garantiza que las operaciones en variables "ordinarias" sean atómicas.

Jean Davy
fuente
2
que enlace solamente se dice arithmeticoperación que consiste en una secuencia de lectura-escritura-actualización de variables 'normales' no son atómicos, no si reado writeel funcionamiento de las variables 'normales' son atómicos o no.
D3Hunter