#include <atomic>
#include <iostream>
#include <thread>
class atomicAcquireRelease00
{
public:
atomicAcquireRelease00() : x(false), y(false), z(0) {}
void run()
{
std::thread a(&atomicAcquireRelease00::write_x, this);
std::thread b(&atomicAcquireRelease00::write_y, this);
std::thread c(&atomicAcquireRelease00::read_x_then_y, this);
std::thread d(&atomicAcquireRelease00::read_y_then_x, this);
a.join();
b.join();
c.join();
d.join();
std::cout<<"z == "<<z.load()<<std::endl;
}
private:
void write_x()
{
x.store(true, std::memory_order_release); //(1)
}
void write_y()
{
y.store(true, std::memory_order_release); //(2)
}
void read_x_then_y()
{
while(!x.load(std::memory_order_acquire)); //(3)
if(y.load(std::memory_order_acquire)){ //(4)
++z;
}
}
void read_y_then_x()
{
while(!y.load(std::memory_order_acquire)); //(5)
if(x.load(std::memory_order_acquire)){ //(6)
++z;
}
}
private:
std::atomic<bool> x, y;
std::atomic<int> z;
};
int main()
{
for(size_t i = 0; i != 50; ++i){
atomicAcquireRelease00().run();
}
return 0;
}
atomicAcquireRelease00
do not respect the order when loading the value. As
far as I know, if I declared the operation store as std::memory_order_release
and operation load as std::memory_order_acquire
as a pair, the operations of
load and store on the same atomic variable will synchronize, but this simple
example doesn't work as I expected.
The process base on my imagination
case A :
case B :
case C :
I can't guarantee x
or y
would be set to true first, but when x
set to true, the load of x
should be synchronized with it as well as y,
so what kind of situation would make z
remain zero?
This is exactly the Sample Listing 5.7 from "Concurrency In Action" by Anthony Williams.
He explains:
In this case the assert can fire (just like in the relaxed-ordering case), because it’s possible for both the load of
x
and the load ofy
to readfalse
. x and y are written by different threads, so the ordering from the release to the acquire in each case has no effect on the operations in the other threads.
C/C++11 does not guarantee a total order of stores to different memory locations unless you use seq_cst. So each thread is free to see the stores in a different order. Acquire-release synchronization doesn't help as it is only from the thread doing the store to the thread doing the load. If you want to play around with unit tests like this and better develop your intuition, try CDSChecker. It is a tool that will show you all behaviors that any real implementation of C11 is likely to produce.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With