Noooooooo
Volatile does not do what you think. All it means is that the compiler will not optimize out memory accesses for that variable. However due to the complexity of the memory pipeline, volatile alone does not guarantee that all previous writes have taken effect before this one. To guarantee this, you
need to use a memory barrier.
This is actually quite a complex topic. But very simply....
any variable used to sync two or more threads must be behind a memory barrier. Ways to put things behind a memory barrier are:
1) make them atomic with std::atomic<> (though it's a new addition to the language... so your compiler might not support it. MSVS doesn't, I don't know about gcc).
2) put all accesses to them behind a mutex or other thread locking mechanism.
3)
some versions of VS add memory blocking functionality to the volatile keyword, but you shouldn't rely on it, as other compilers do not (and not even all versions of VS do it!)
This will bite you in the ass in a big way if you don't get on top of it right away. So do it right!
EDIT: I just did a bunch of reading and research on this topic... and there are a LOT of bad/incorrect tutorials floating around, even from seemingly credible sources. If you are learning multithreading from a tutorial that is saying you don't need this...
it's wrong.
EDIT 2: If your compiler does not support std::atomic, here is a miniature version of it I made that has a similar effect. It will properly ensure all accesses to this variable are threadsafe:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51
|
// note... use:
// namespace threadlib = std;
// if your compiler supports C++11 threads.
// otherwise use:
// namespace threadlib = boost;
// if it doesn't (but then you need to install boost)
// or you can change the below mutexes to match whatever thread lib you're using.
template <typename T>
class Atomic
{
public:
Atomic(T x = T()) : v(x) { }
Atomic(const Atomic<T>& x) : v(x) { }
operator T () const
{
T copy;
{
threadlib::lock_guard<threadlib::mutex> lk(m);
copy = v;
}
return copy;
}
void operator = (const T& x)
{
threadlib::lock_guard<threadlib::mutex> lk(m);
v = x;
}
void operator = (const Atomic<T>& x)
{
*this = static_cast<T>(x);
}
private:
volatile T v;
mutable threadlib::mutex m;
};
// typical usage:
atomic<bool> stop;
// then just use it as if it were a normal bool:
stop = true;
if(stop == false)
{
//...
}
|
EDIT 3:
And of course... for performance reasons... access to these variables should be minimized because it is
extremely expensive.