Unfortunately, Matt responds to functions called double-check locking, which is not supported by the C / C ++ memory model. (This is supported by Java 1.5 and later, and I think it is a .NET memory model.) This means that meanwhile, when the pObj == NULL
check is performed and when the lock (mutex) is obtained, pObj
may already have assigned to another thread. Switching flows occurs whenever the OS wants this, and not between the "lines" of the program (which do not matter after compilation in most languages).
Also, as Matt admits, he uses int
as a lock, not an OS primitive. Do not do this. Proper locks require the use of memory protection instructions that are potentially associated with a line cache, etc .; use operating system primitives to lock. This is especially important because the primitives used can vary between the individual lines of the CPU that your operating system runs on; what works on CPU Foo may not work on CPU Foo2. Most operating systems either support POSIX threads (pthreads) or offer them as a wrapper for the OS streaming package, so it is often best to illustrate examples using them.
If your operating system offers the appropriate primitives, and if you absolutely need it for performance, instead of doing this type of lock / initialization, you can use the atomic comparison and swap operation to initialize a common global variable. Essentially, what you write will look like this:
MySingleton *MySingleton::GetSingleton() { if (pObj == NULL) {
This only works if it is safe to create multiple instances of your singleton (one per thread, which calls GetSingleton () at the same time) and then discards additional data. The OSAtomicCompareAndSwapPtrBarrier
function provided on Mac OS X — most operating systems provide a similar primitive — checks to see if pObj
NULL
and only actually sets it to temp
, if so. It uses hardware support to really, literally just perform an exchange and say if it happened.
Another tool to use if your OS suggests that between these two extremes is pthread_once
. This allows you to configure a function that runs only once - basically, doing all the locks / barriers / etc. the trick for you - no matter how many times it gets called or how many threads it gets called.
Chris Hanson Aug 09 '08 at 23:09 2008-08-09 23:09
source share