The official answer is: If you're sharing a (non-atomic) boolean variable across threads and not serializing access to that variable (e.g. with a mutex), then you've invoked undefined behavior, and the program is free to do whatever it likes (work as expected, work some other way, crash, steal cash from your wallet, etc).
The more practical answer is: On a modern multi-core machine, each core has its own registers and a separate L1 cache, and so if a thread running on core 1 sets a particular memory location, then another thread running on core 2 may not "see" that change unless the compiler has taken specific steps to make sure that the change is propagated across cores. Furthermore, unless you (the programmer) have taken explicit measures to let the compiler know that a particular variable is going to be used by multiple threads, the compiler's optimizer may assume that that variable cannot be changed outside of a given thread's flow of execution, and may therefore remove your test of the variable's state entirely (because after all, if the compiler has "proven" that the variable's value cannot be changed, why waste CPU cycles checking its state?).
What's likely happening in your case is that the calls to printf()
or usleep()
have side effects (e.g. a usermode->kernel->usermode switch) that include flushing the core's cache, such that the second thread (at least eventually) "sees" the change made by the first thread -- while without those calls, there is no reason to synchronize caches and thus the update is never "seen".
The practical advice: If you're going to share a variable across threads, be sure to either use a std::atomic<bool>
instead of a plain-old bool
, or serialize/protect all accesses (reads and writes) to that variable inside a mutex or critical section. Otherwise you're likely to run into lots "interesting" behavior of this sort.