c++ - numero - peso atomico ejemplos
Atómica bool no protege el contador no atómico (1)
Encontré un problema con un mutex spinlock (básico) que no parece funcionar como se esperaba.
4 hilos están incrementando un contador no atómico que está protegido por este mutex. El resultado no coincide con el resultado esperado que hace que el mutex parezca roto.
salida de ejemplo:
result: 2554230
expected: 10000000
En mi entorno sucede bajo las siguientes condiciones:
flag
esstd::atomic<bool>
, cualquier otra cosa comostd::atomic<int>
ostd::atomic_flag
(contest_and_set
) funciona bien.compilado en X86_64 con gcc 6.3.1 y
-O3
flag
Mi pregunta es, ¿qué podría explicar este comportamiento?
#include <iostream>
#include <vector>
#include <atomic>
#include <thread>
#include <mutex>
#include <assert.h>
class my_mutex {
std::atomic<bool> flag{false};
public:
void lock()
{
while (flag.exchange(true, std::memory_order_acquire));
}
void unlock()
{
flag.store(false, std::memory_order_release);
}
};
my_mutex mut;
static int counter = 0;
void increment(int cycles)
{
for (int i=0; i < cycles; ++i)
{
std::lock_guard<my_mutex> lck(mut);
++counter;
}
}
int main()
{
std::vector<std::thread> vec;
const int n_thr = 4;
const int n_cycles = 2500000;
for (int i = 0; i < n_thr; ++i)
vec.emplace_back(increment, n_cycles);
for(auto &t : vec)
t.join();
std::cout << " result: " << counter << std::endl;
std::cout << "expected: " << n_cycles * n_thr << std::endl;
}
editar
Por solicitud de Voo, aquí está la salida del ensamblaje para increment()
..
$ g++ -O3 increment.cpp
$ gdb a.out
Reading symbols from a.out...done.
(gdb) disassemble increment
Dump of assembler code for function increment(int):
0x0000000000401020 <+0>: mov 0x20122a(%rip),%ecx # 0x602250 <_ZL7counter>
0x0000000000401026 <+6>: test %edi,%edi
0x0000000000401028 <+8>: mov $0x1,%edx
0x000000000040102d <+13>: lea (%rdi,%rcx,1),%esi
0x0000000000401030 <+16>: jle 0x401058 <increment(int)+56>
0x0000000000401032 <+18>: nopw 0x0(%rax,%rax,1)
0x0000000000401038 <+24>: mov %edx,%eax
0x000000000040103a <+26>: xchg %al,0x20120c(%rip) # 0x60224c <mut>
0x0000000000401040 <+32>: test %al,%al
0x0000000000401042 <+34>: jne 0x401038 <increment(int)+24>
0x0000000000401044 <+36>: add $0x1,%ecx
0x0000000000401047 <+39>: cmp %ecx,%esi
0x0000000000401049 <+41>: mov %ecx,0x201201(%rip) # 0x602250 <_ZL7counter>
0x000000000040104f <+47>: movb $0x0,0x2011f6(%rip) # 0x60224c <mut>
0x0000000000401056 <+54>: jne 0x401038 <increment(int)+24>
0x0000000000401058 <+56>: repz retq
End of assembler dump.
Su código es correcto. Es un error 80004 - [6 Regresión] carga no atómica movida a antes de carga atómica con std :: memory_order_acquire