Racing data in a simple spinlock using atomic <bool>
#include<atomic>
#include<thread>
#include<vector>
#include<iostream>
#include<algorithm>
#include<mutex>
using namespace std;
class spinlock
{
private:
atomic<bool> flag;
public:
spinlock():flag(false)
{
}
void lock()
{
bool temp=false;
while(!flag.compare_exchange_weak(temp, true, std::memory_order_seq_cst) && !temp);
}
void unlock()
{
flag.store(false, std::memory_order_seq_cst);
}
};
int main()
{
spinlock sp;
//mutex sp;
std::vector<std::thread> threads;
int count=0;
std::cout << "increase global counter with 10 threads...\n";
for (int i=1; i<=10000; ++i)
{
threads.push_back(std::thread([&sp,&count](){for(int i=0;i<10;++i){sp.lock();++count;sp.unlock();}}));
}
for_each(threads.begin(),threads.end(),[](thread &t){t.join();});
cout<<count<<endl;
return 0;
}
The above code creates 10k streams and uses them to increment the counter. I run it on an x86_64 machine with gcc 5.2, I tried code with different memory orders and compare_exchange_weak / strong. None of them expected a result of 100 thousand. When I perform the same experiment with a mutex, I get the correct result. Why don't I get the correct result here if the atomic atom is really atomic and the memory ordering seq_cst is the strongest available here that I used here?
Edit (fixed code):
void lock()
{
bool temp=false;
while(!flag.compare_exchange_weak(temp, true, std::memory_order_seq_cst))
{
temp=false;
}
}
- , temp- , temp , . .
+4
1