I need to set a flag for another thread to exit. That other thread checks the exit flag from time to time. Do I have to use atomic for the flag or just a plain bool is enough and why (with an example of what exactly may go wrong if I use plain bool)?
#include <future> bool exit = false; void thread_fn() { while(!exit) { //do stuff if(exit) break; //do stuff } } int main() { auto f = std::async(std::launch::async, thread_fn); //do stuff exit = true; f.get(); }
You need atomic<bool> to avoid race-conditions. A race-condition occurs if two threads access the same memory location, and at least one of them is a write operation. If your program contains race-conditions, the behavior is undefined.
As stated above, bool is atomic, but you still need to remember that it also depends on what you want to do with it. is not an atomic operation, meaning that the value of b could change before the current thread executes the code after the if statement.
std::atomic compiles to lock addq . The LOCK prefix makes the following inc fetch, modify and update memory atomically. our explicit inline assembly LOCK prefix compiles to almost the same thing as std::atomic , except that our inc is used instead of add .
Do I have to use atomic for “exit” bool variable?
Yes.
Either use atomic<bool>
, or use manual synchronization through (for instance) an std::mutex
. Your program currently contains a data race, with one thread potentially reading a variable while another thread is writing it. This is Undefined Behavior.
Per Paragraph 1.10/21 of the C++11 Standard:
The execution of a program contains a data race if it contains two conflicting actions in different threads, at least one of which is not atomic, and neither happens before the other. Any such data race results in undefined behavior.
The definition of "conflicting" is given in Paragraph 1.10/4:
Two expression evaluations conflict if one of them modifies a memory location (1.7) and the other one accesses or modifies the same memory location.
Yes, you must have some synchronization. The easiest way is, as you say, with atomic<bool>
.
Formally, as @AndyProwl says, the language definition says that not using an atomic here gives undefined behavior. There are good reasons for that.
First, a read or write of a variable can be interrupted halfway through by a thread switch; the other thread may see a partly-written value, or if it modifies the value, the original thread will see a mixed value. Second, when two threads run on different cores, they have separate caches; writing a value stores it in the cache, but doesn't update other caches, so a thread might not see a value written by another thread. Third, the compiler can reorganize code based on what it sees; in the example code, if nothing inside the loop changes the value of exit
, the compiler doesn't have any reason to suspect that the value will change; it can turn the loop into while(1)
.
Atomics address all three of these problems.
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