c++ c gcc x86 x86-64

c++ - ¿El incremento es un entero atómico en x86?



gcc x86-64 (4)

Aquí hay una prueba de que no es atómico en una implementación particular (gcc). Como puedes ver (?), Gcc genera un código que

  1. carga el valor de la memoria a un registro
  2. Incrementa el contenido del registro.
  3. guarda el registro de nuevo en la memoria.

Eso está muy lejos de ser atómico.

$ cat t.c volatile int a; void func(void) { a++; } [19:51:52 0 ~] $ gcc -O2 -c t.c [19:51:55 0 ~] $ objdump -d t.o t.o: file format elf32-i386 Disassembly of section .text: 00000000 <func>: 0: a1 00 00 00 00 mov 0x0,%eax 5: 83 c0 01 add $0x1,%eax 8: a3 00 00 00 00 mov %eax,0x0 d: c3 ret

No se deje engañar por el 0x0 en la instrucción mov , hay espacio para 4 bytes allí, y el enlazador completará la dirección de memoria resultante para a allí cuando este archivo de objeto esté vinculado.

Esta pregunta ya tiene una respuesta aquí:

En una máquina x86 multinúcleo, Say un subproceso que se ejecuta en core1 incrementa una variable entera a la vez que un thread en el core 2 también lo incrementa. Dado que el valor inicial de a era 0, ¿sería siempre 2 al final? ¿O podría tener algún otro valor? Supongamos que a se declara como volatile y no estamos utilizando variables atómicas (como atomic <> de C ++ y operaciones atómicas integradas en gcc).

Si el valor de a sería en realidad siempre 2 en tal caso, ¿significa eso que un long int en x86-64 también tendría la misma propiedad, es decir, a siempre será 2 al final?


La instrucción de máquina de memoria incrementada en un X86 es atómica solo si se usa con un prefijo LOCK .

x ++ en C y C ++ no tiene comportamiento atómico. Si realiza incrementos desbloqueados, debido a las carreras en las que el procesador está leyendo y escribiendo X, si dos procesadores independientes intentan un incremento, puede terminar con solo un incremento o ver ambos (el segundo procesador puede haber leído el valor inicial, incrementado y lo escribió de nuevo después de que el primero vuelve a escribir sus resultados).

Creo que C ++ 11 ofrece incrementos atómicos, y la mayoría de los compiladores de proveedores tienen una forma idiomática de causar un incremento atómico de ciertos tipos de enteros incorporados (típicamente int y long); Consulte el manual de referencia de su compilador.

Si desea incrementar un "valor grande" (por ejemplo, un entero de multiprecisión), debe hacerlo utilizando algún mecanismo de bloqueo estándar, como un semáforo.

Tenga en cuenta que también debe preocuparse por las lecturas atómicas. En el x86, la lectura de un valor de 32 o 64 bits resulta ser atómica si está alineada con una palabra de 64 bits. Eso no será verdad de un "gran valor"; de nuevo necesitarás un bloqueo estándar.


No está garantizado. Puede usar la instrucción lock xadd para lograr el mismo efecto, o usar C ++ std::atomic , o usar #pragma omp atomic , o cualquier número de otras soluciones de concurrencia que se hayan escrito para evitarle el problema de reinventar la rueda.


Ya que nadie ha respondido a tu pregunta actual y, en cambio, te está mostrando cómo hacerlo de una manera que siempre funciona:

El hilo 1 carga el valor de 0

Rosca 2 cargas valor de 0

El hilo 1 incrementa las tiendas 1

El subproceso 2 incrementa su registro local de valor y almacena 1.

Como puede ver, el resultado final es un valor igual a 1 y no a 2. No siempre será 2 al final.