Re: Confused about a thread-safe singleton example.
On Dec 2, 9:17 pm, Sam <s...@email-scan.com> wrote:
jason.cipri...@gmail.com writes:
static Mutex mutex;
static TheClass *instance;
static TheClass * getInstance () {
MutexLocker lock(mutex);
if (!instance)
instance = new TheClass();
return instance;
}
The example then goes on to talk about how double-check locking is
broken, etc. My question is pretty much this: Is C++ static
initialization thread-safe? If not, then how does the above example
safely use "mutex"? If so, then what is wrong with this:
static TheClass instance; // not a pointer
static TheClass * getInstance () {
return &instance; // it's correctly initialized?
}
The reason I ask is I almost never see it done like that, I always see
blog entries and articles that say the same thing "store instance in a
pointer, use a mutex to protect, and p.s. double-checked locking is
broken". It seems like doing it lock-free is made out to be a hard
problem, so *if* having a static instance works (but I don't know if
it does, that's my question), then why doesn't anybody ever suggest
it?
Setting aside the fact that there's no such thing as threads or mutexes i=
n
the C++ language (at least not yet), so you are using a platform specific
library here.
I just used "Mutex" and "AutoMutex" as an example.
Your statically declared instance gets constructed at some unspecified po=
int
before your main() function gets invoked. If you have other objects in
static scope, it is unspecified the order in which all your static instan=
ces
get initialized. This may be undesirable. It's possible that it is necess=
ary
to construct your singleton in a more controlled fashion, after all your
other objects, in static scope or otherwise, get initialized. Using a
dynamically-allocated pointer to your singleton, and protecting it with a
mutex, gives you the means to accomplish that.
I see. So, it's safe to use a global-scoped static instance for the
singleton instance, as long as you don't need *precise* control over
when it's initialized (just as long as it's initialized before it's
used)? Even if it's accessed from different translation units (and
defined in a different one than main() is in)?
I did an experiment with VS 2008 where I made the singleton class's
constructor Sleep() for 2 seconds hoping to make a race condition
occur, and did this:
=== A.h ===
class A {
public:
A ();
~A ();
static A * getInstance ();
};
=== A.cpp ===
static A theInstance;
A * A::getInstance () { return &theInstance; }
I had main() in a different source file, and it created some threads
with functions in a 3rd source file. I called A::getInstance() in each
of those threads, and saw that theInstance was initialized before main
() was even entered, and everything worked great.
Is this standard behavior that I can rely on, or is it specific to the
MS compiler?
I also tried making theInstance function-scoped, in the getInstance()
function. That didn't work, I guess there's different rules for
function-scoped static initialization (I did read that, and also read C
++0x makes some guarantees about it). I noticed that if I created
multiple threads like this:
threadproc () {
A * a = A::getInstance();
}
The first thread created waited the 2 seconds as the A() was
constructed, but every thread after that immediately returned, *with*
the pointer, but before the A() constructor had returned.
A * A::getInstance () {
static A theInstance;
return &theInstance;
}
I guess that makes sense. "&theInstance" is already known, so threads
can return immediately while it's still being constructed.
The third thing I tried was storing theInstance at class scope, and
using a pointer but statically initializing it with new(). That also
worked, it was initialized before main() was entered. Is this also
behavior that can be relied on? E.g.:
class A {
static A * getInstance ();
...
static A * theInstance;
};
A * A::theInstance = new A();
A * A::getInstance () { return theInstance; }
So, global scope worked, class scope worked, function scope was all
messed up.
Thanks,
Jason