Can you give you your opinion, please? What would you do differently? I mean, do you think it would be better if I have done it with std::task or std::mutex, std::condition_variable, etc? I an overkill to control the threads with 2 flags?
std::atomic<int> counter = { 0 };
std::atomic<bool> switchFlag = { false };
std::atomic<bool> finished = { false };
constexpr int MAX_NUM = 10;
void increment(){
while (!finished.load()){
if (!switchFlag.load()){
std::cout << "incremented to =" << ++counter << '\n';
switchFlag.store(true);
}
}
}
void print(){
while (!finished.load()) {
if (switchFlag.load()){
std::cout << "counter=" << counter.load() << '\n';
if (counter.load() >= MAX_NUM)
finished.store(true);
switchFlag.store(false);
}
}
}
int main() {
auto t1 = std::thread(increment);
auto t2 = std::thread(print);
t1.join();
t2.join();
return 0;
}
This is, to be blunt, unbelievably awful on typical, realistic hardware. The most obvious problem is this:
Look at the thread in increment
. Until print
runs, the if
will be false
and the while
will be true. To branch prediction will begin becoming firmly convinced that the if
will be false
.
Then, when the print
thread sets switchFlag
to false
and you need increment
to execute as quickly as possible because the other thread is going to be waiting for it, you take the worst mispredicted branch imaginable.
So at the very instant where it is is the most critical that you execute as quickly as possible, you encounter the very worst performance the processor can give you, blowing out all the pipelines with a mispredicted branch.
I would strongly urge you not to try to compose complex operations out of primitive operations like atomic loads and stores. Doing so requires deep platform expertise. Use high-level functions (like mutexes and condition variables) for high-level operations (like waiting).
But there's never any good way to implement two threads that need to alternate execution. For any case where two threads can't ever concurrently make forward progress, there's no good reason to have two threads.