What you know is false. Volatile is not used to synchronize memory access between threads, apply any kind of memory fences, or anything of the sort. Operations on volatile
memory are not atomic, and they are not guaranteed to be in any particular order. volatile
is one of the most misunderstood facilities in the entire language. "Volatile is almost useless for multi-threadded programming."
What volatile
is used for is interfacing with memory-mapped hardware, signal handlers and the setjmp
machine code instruction.
It can also be used in a similar way that const
is used, and this is how Alexandrescu uses it in this article. But make no mistake. volatile
doesn't make your code magically thread safe. Used in this specific way, it is simply a tool that can help the compiler tell you where you might have messed up. It is still up to you to fix your mistakes, and volatile
plays no role in fixing those mistakes.
EDIT: I'll try to elaborate a little bit on what I just said.
Suppose you have a class that has a pointer to something that cannot change. You might naturally make the pointer const:
class MyGizmo
{
public:
const Foo* foo_;
};
What does const
really do for you here? It doesn't do anything to the memory. It's not like the write-protect tab on an old floppy disc. The memory itself it still writable. You just can't write to it through the foo_
pointer. So const
is really just a way to give the compiler another way to let you know when you might be messing up. If you were to write this code:
gizmo.foo_->bar_ = 42;
...the compiler won't allow it, because it's marked const
. Obviously you can get around this by using const_cast
to cast away the const
-ness, but if you need to be convinced this is a bad idea then there is no help for you. :)
Alexandrescu's use of volatile
is exactly the same. It doesn't do anything to make the memory somehow "thread safe" in any way whatsoever. What it does is it gives the compiler another way to let you know when you may have screwed up. You mark things that you have made truly "thread safe" (through the use of actual synchronization objects, like Mutexes or Semaphores) as being volatile
. Then the compiler won't let you use them in a non-volatile
context. It throws a compiler error you then have to think about and fix. You could again get around it by casting away the volatile
-ness using const_cast
, but this is just as Evil as casting away const
-ness.
My advice to you is to completely abandon volatile
as a tool in writing multithreadded applications (edit:) until you really know what you're doing and why. It has some benefit but not in the way that most people think, and if you use it incorrectly, you could write dangerously unsafe applications.
volatile
cannot be used as a poor man's thread synchronization (although compilers might extend its meaning thus). Writing to avolatile
object in one thread does not necessarily mean another thread will see the updated value. (It might only have been written to one CPU's cache, but not through the cache into whatever memory the CPUs share.) For that you need memory barriers. – Sandbergvolatile
: writes need to make it to main memory. The reason that it cannot be used for synchronization is that the guarantees don't ensure atomicity or reorders with non-volatile variables. – Esauvolatile
writes to main memory. – Cleavagevolatile
is often used for addresses that do not even correspond to memory, so I think this must be wrong. But, yes, atomicity and write ordering are problems I forgot about. – Sandbergmemory_order_relaxed
. GCC/clang at least also make an effort to do the load or store as a single access that will be atomic if the HW guarantees it.) – Biotechnology