A singleton ensures that only one object of a certain type exists throughout the program. Double-checked locking is a common, efficient way to initialize a singleton in multithreaded applications. The following code illustrates such an implementation.
100 class Singleton {
101 public:
102 static Singleton* instance();
103 ...
104 private:
105 static Singleton* ptr_instance;
106 };
107
108 Singleton* Singleton::ptr_instance = 0;
...
200 Singleton* Singleton::instance() {
201 Singleton *tmp;
202 if (ptr_instance == 0) {
203 Lock();
204 if (ptr_instance == 0) {
205 tmp = new Singleton;
206
207 /* Make sure that all writes used to construct new
208 Singleton have been completed. */
209 memory_barrier();
210
211 /* Update ptr_instance to point to new Singleton. */
212 ptr_instance = tmp;
213
214 }
215 Unlock();
216 }
217 return ptr_instance;
The read of ptr_instance on line 202 is intentionally not protected by a lock. This makes the check to determine whether or not the Singleton has already been instantiated in a multithreaded environment more efficient. Notice that there is a data race on variable ptr_instance between the read on line 202 and the write on line 212, but the program works correctly. However, writing a correct program that enables data races requires extra care. For example, in the above double-checked-locking code, the call to memory_barrier() at line 209 is used to ensure that ptr_instance is not seen to be non-null by the threads until all writes to construct the Singleton have been completed.