c++ - que - ¿Cuál es la diferencia entre++, add operation y fetch_add() en atomic()
i++ java significa (3)
operator ++ no es una sola operación, pero 3 operaciones cargan add store, y por ej. en arm64 single load o almacen dos no generan ninguna valla de datos, la memoria de datos es más pequeña. for ex atomic_add 1 es un grupo de códigos con semántica de aquire / release
.LBB2_1:
ldaxr x8, [x0] //load exclusive register with aquire
add x8, x8, #1
stlxr w9, x8, [x0] //store with rlease
cbnz w9, .LBB2_1 //if another thread changed value, try again
donde el operador ++ causará una condición de carrera si se usa de forma simulada por 2 hilos
ldr x8, [x0]
add x8, x8, #1 // =1
str x8, [x0]
Ejecuté el siguiente código muchas veces, pero ¿por qué el resultado del incremento de prefijo, fetch_add () muestra el resultado correcto mientras que con la operación de adición (+), se imprime el resultado incorrecto?
#include <iostream>
#include <mutex>
#include <future>
using namespace std;
atomic <int> cnt (0);
void fun()
{
for(int i =0; i <10000000 ; ++i)
{
//++cnt; // print the correct result 20000000
//cnt = cnt+1; // print wrong result, arbitrary numbers
cnt.fetch_add(1); // print the correct result 20000000
}
}
int main()
{
auto fut1 = async(std::launch::async, fun);
auto fut2 = async(std::launch::async, fun);
fut1.get();
fut2.get();
cout << "value of cnt: "<<cnt <<endl;
}
++cnt
y cnt.fetch_add(1)
son operaciones verdaderamente atómicas. Un hilo se bloquea mientras el otro hilo lee, incrementa y actualiza el valor. Como tal, los dos hilos no pueden pisar los dedos de los pies. El acceso a cnt
está completamente serializado, y el resultado final es el que usted esperaría.
cnt = cnt+1;
No es completamente atómico. Implica tres operaciones separadas, solo dos de las cuales son atómicas, pero una no lo es. Cuando un hilo ha leído atómicamente el valor actual de cnt
e hizo una copia local, el otro hilo ya no está bloqueado y puede modificar libremente cnt
a voluntad mientras se incrementa esa copia . Luego, la asignación de la copia incrementada a cnt
se realiza de forma atómica, pero se asignará un valor obsoleto si cnt
ya ha sido modificado por el otro hilo. Así que el resultado final es aleatorio y no lo que se esperaría.
cnt = cnt+1
Esta no es una operación atómica. Esto primero carga cnt
en una operación atómica, luego hace la suma y finalmente almacena el resultado en otra operación atómica. Sin embargo, el valor puede cambiarse después de la carga, que puede ser sobrescrito por la tienda final, lo que conduce a un resultado final incorrecto.
Los otros dos son operaciones atómicas y por lo tanto evitan tal condición de carrera.
Tenga en cuenta que, el operador ++, --, +=, -=, &=, |=, ^=
están sobrecargados en std::atomic
para proporcionar operaciones atómicas.