There are three question about the code snippet below.
NO_STUCK_WITH_OPTIMIZATION
) is not enabled, why this code snippet gets stuck when the optimization is enabled(i.e. -O1
, -O2
or -O3
) whereas the program works well if the optimization is not enabled?std::this_thread::sleep_for()
is added?UPDATED:
3. If the is_run
is declared as volatile
(for details, see the code snippet), then the program would never get stuck on X86?
^^UPDATED ENDS^^
#include <functional>
#include <thread>
#include <iostream>
#include <chrono>
#include <atomic>
#ifdef NO_STUCK_WITH_OPTIMIZATION
using TYPE = std::atomic<int>;
#else
using TYPE = int; //The progrom gets stuck if the optimization is enabled.
#endif
int main()
{
TYPE is_run{1};
auto thread = std::thread([&is_run](){while(1==is_run){
//std::this_thread::sleep_for(std::chrono::milliseconds(10)); //If this line is added, the program is no longer gets stuck. Why?
}
std::cout << "thread game over" << std::endl;
});
std::this_thread::sleep_for(std::chrono::seconds(1));
is_run = 0;
thread.join();
}
You have a multi-threaded program. One thread does is_run = 0;
.
The other thread does while(1==is_run)
. Although you guarantee, with a sleep (matter for another question), that the write is done before the read, you need to tell the compiler to synchronize this variable.
In C++, the simple way to make sure one thread sees the change is to use atomic<int>
. If you don't do this, the other thread might never see the change. This hiding of the change might be due to local optimization of the code at compile time, by the OS deciding not to refresh some memory page or by the hardware itself deciding the memory doesn't need to be reloaded.
Putting the atomic variable guarantees all those systems know what you want to do. So, use it. :-)
Lifting from the comments:
https://en.cppreference.com/w/cpp/language/memory_model#Threads_and_data_races
A program that has two conflicting evaluations has a data race unless both evaluations execute on the same thread or in the same signal handler, or both conflicting evaluations are atomic operations (see std::atomic), or one of the conflicting evaluations happens-before another (see std::memory_order) If a data race occurs, the behavior of the program is undefined.