I wrote a simple multithreading programs as follows:
static bool finished = false; int func() { size_t i = 0; while (!finished) ++i; return i; } int main() { auto result=std::async(std::launch::async, func); std::this_thread::sleep_for(std::chrono::seconds(1)); finished=true; std::cout<<"result ="<<result.get(); std::cout<<"\nmain thread id="<<std::this_thread::get_id()<<std::endl; }
It behaves normally in debug mode in Visual studio or -O0
in gcc and print out the result after 1
seconds. But it stuck and does not print anything in Release mode or -O1 -O2 -O3
.
The run() method of thread class is called if the thread was constructed using a separate Runnable object otherwise this method does nothing and returns. When the run() method calls, the code specified in the run() method is executed. You can call the run() method multiple times.
Parallel processing using many threads can greatly improve program performance, but it may also make debugging more difficult because you're tracking many threads. Multithreading can introduce new types of potential bugs.
In the same multithreaded process in a shared-memory multiprocessor environment, each thread in the process can run concurrently on a separate processor, resulting in parallel execution, which is true simultaneous execution.
Two threads, accessing a non-atomic, non-guarded variable are U.B. This concerns finished
. You could make finished
of type std::atomic<bool>
to fix this.
My fix:
#include <iostream> #include <future> #include <atomic> static std::atomic<bool> finished = false; int func() { size_t i = 0; while (!finished) ++i; return i; } int main() { auto result=std::async(std::launch::async, func); std::this_thread::sleep_for(std::chrono::seconds(1)); finished=true; std::cout<<"result ="<<result.get(); std::cout<<"\nmain thread id="<<std::this_thread::get_id()<<std::endl; }
Output:
result =1023045342 main thread id=140147660588864
Live Demo on coliru
Somebody may think 'It's a bool
– probably one bit. How can this be non-atomic?' (I did when I started with multi-threading myself.)
But note that lack-of-tearing is not the only thing that std::atomic
gives you. It also makes concurrent read+write access from multiple threads well-defined, stopping the compiler from assuming that re-reading the variable will always see the same value.
Making a bool
unguarded, non-atomic can cause additional issues:
atomic<bool>
with memory_order_relaxed
store/load would work, but where volatile
wouldn't. Using volatile for this would be UB, even though it works in practice on real C++ implementations.)To prevent this to happen, the compiler must be told explicitly not to do.
I'm a little bit surprised about the evolving discussion concerning the potential relation of volatile
to this issue. Thus, I'd like to spent my two cents:
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