-2

How can I set the flag within the main method but still have the while loop recognize the change?

static bool flag = true;

void method()
{
   while (flag) {
 // do something 
   std::this_thread::sleep_for(10s);
   }
}

int main()
{
   std::thread method_thread(method);
   flag = false;
   method_thread.join();
   return 1;
}
mstudent
  • 7
  • 2

1 Answers1

1

Since you didn't declare flag as std::atomic<bool>, the compiler is allowed to assume that the variable won't be changed by another thread, unless these accesses are separated by synchronization points.

For this reason, it is likely that the compiler decided to optimize the while loop of the function method in the following way: Instead of reading flag from memory in every loop iteration, it will only read it once from memory and will cache it in a CPU register, so that it only has to check the value in the CPU register in every loop iteration (which is faster than reading from memory). Therefore, this thread may not notice when flag is changed by another thread, because its cached copy will remain unchanged.

Or possibly, the compiler was smart enough to notice that flag will never change in the while loop. As previously mentioned, it is allowed to assume that no other thread is changing it. Therefore, the compiler might optimize that check away, so that is calls std::this_thread::sleep_for in an infinite loop without ever checking flag.

Declaring the variable to be atomic will force the compiler to take the possiblity into account that the variable could be modified by another thread at any time. Therefore, the compiler will be prevented from performing any of the optimizations mentioned above and will be forced to reload the variable every time from memory, instead of using a cached copy.

According to the ISO C++ standard, accessing the same memory location by several threads in a non-atomic fashion in which not all threads are read-only will cause undefined behavior. Therefore, the standard requires you to use std::atomic in such a situation.

Andreas Wenzel
  • 22,760
  • 4
  • 24
  • 39
  • 2
    `volatile` has nothing to do with multi-threading or even atomicity. A struct with two elements can be declared volatile and will probably not be atomic. The only purpose of volatile is to make the memory access part of the observable behavior of the program and thus prevent the compiler from eliminating accesses or reorder them. It does not make something atomic or coherent across threads. It even does not stop the CPU from reordering your code: https://stackoverflow.com/questions/4557979/when-to-use-volatile-with-multi-threading. – Jens Oct 14 '20 at 21:28
  • @Jens: In this situation, I do believe that using `volatile` would have prevented the issue from occuring. I don't think that memory reordering could be responsible for the issue, because memory access can't be reordered over system calls. But it is still undefined behavior. Therefore, you are probably right that it is better if I only mention `std::atomic` and not `volatile`. I have therefore removed it from may answer. – Andreas Wenzel Oct 14 '20 at 22:08
  • In addition to the issues I mentioned concerning reordering and atomicitly, `volatile` also does not improve anything about data visibility or coherence. It is totally possible that while volatile enforces the actual write instruction being put into the code and it being executed by the CPU, another thread will not see the change – Jens Oct 16 '20 at 20:25