A simple situation here, If I got three threads, and one for window application, and I want them to quit when the window application is closed, so is it thread-safe if I use one global variable, so that three threads will quit if only the global variable is true, otherwise continue its work? Does the volatile help in this situation? C++ programming.
If you only want to "read" from the shared variable from the other threads, then it's ok in the situation you describe.
Yes the volatile hint is required or the compiler might "optimize out" the variable.
Waiting for the threads to finish (i.e. join
) would be good too: this way, any clean-up (by the application) that should occur will have a chance to get done.
Yes this is a common technique.
But you should also wait for all child threads to exit before the main thread exits main().
In most thread implementations if the main thread exits main() all currently live child threads are repeated (see your threading documentation for details) without the benefit of allowing their stacks to unwind correctly. Thus all the nice benefits of RAII will be lost.
So set your global variable, but then wait (most threading systems have a join method to allow you to wait (for threads in a non busy state) to die) for all children to exit cleanly before allowing the main() thread to exit.
It's safe right up to the point that you change the variable's value to get the threads to quit. At that point 1) you need to synchronize access, and 2) you need to do something (sorry, volatile isn't enough) to assure that the new value gets propagated to the other threads correctly.
The former part is pretty easy. The latter substantially more difficult -- to the point that you'll almost certainly need to use some sort of library- or OS-provided mechanism.
No, this is risky because of memory visibility issues. On a multi-processor, writing to memory on one processor does not mean a different processor will see that change immediately. Furthermore, without using mutex's, it's possible that it can take quite a long time before the change is propagated to the other processors.
Theoretically, volatile
is not enough. There are two abstraction layers:
- between the source code actions and the actual opcodes;
- between what a core/processor sees and what the other cores/processors see.
The compiler is free to cache data in register and reorder read and writes. By using volatile
you instruct the compiler to produce opcodes which perform the read and writes exactly in the order you specify in your source code. But this handles only the first layer. The hardware system which manages communication between the processor cores may also delay and reorder reads and writes.
It so happens that on x86 hardware, cores propagate writes to main memory fairly fast, and other cores are automatically notified that memory has changed. So that volatile
appears to be enough: it makes sure that the compiler will not play funky games with registers, and the memory system is kind enough to handle things from that point. Note, though, that this is not true on all systems (I think that at least some Sparc systems could delay write propagation for arbitrary delays -- possibly hours) and I have read in one of the AMD manuals that AMD explicitly reserves the right to propagate writes less promptly in some future processors.
So the clean solution is to use a mutex (pthread_mutex_lock()
on Unix, EnterCriticalSection()
on Windows) whenever accessing your global variable (both for reading and for writing). Mutex primitives include a special operation known as a memory barrier, which is like a volatile
on steroids (it acts as a volatile
for both abstraction layers).