Singleton design pattern: Thread-safe Singleton
Microsoft .NET Framework, ASP.NET, Visual C# (CSharp, C Sharp, C-Sharp) Developer Training, Visual Studio
| C# Design Patterns |
|
| edit |
.NET is multithreaded meaning that more than one thread can execute our Singleton concurrently. This means that we never know—and don't usually care—that our thread may be starting and stopping with other threads running in the gaps.
The danger is that one thread can execute the if (singleton == null) test in line 4 and—finding it null—proceed to the new Singleton() in line 6. But, the thread scheduler can interrupt the first thread at that point and resume execution of a previously interrupted second thread at line 4. Since, no new Singleton() has yet been executed, the second thread also finds singleton to be null. The result is that both threads will execute the new Singleton(); and, two Singletons will be instantiated which—of course—defeats the whole purpose of the pattern.
// WARNING: This code is not thread safe! public static Singleton GetInstance() { if (singleton == null) { // System can change threads here ! singleton = new Singleton(); } return singleton; }
Fortunately, there are three ways of dealing with the thread safety issue.
Thread-safe Singleton version 1
This is the eager implementation which we have already seen. This instantiates the Singleton class before any threads can run on it. Even though static class members are initialized in a lazy manner, the compiler guarantees the thread safety.
This method is the easiest and is suitable for almost all situations.
public sealed class Singleton { private static readonly Singleton singleton = new Singleton(); // eager static Singleton() {} private Singleton() {} public static Singleton GetInstance() { return singleton; } }
Thread-safe Singleton version 2
This implementation uses a lock. We are locking on a special object—the singletonLock—to avoid any risk of deadlock or performance issue associated with locking on a typeof(Singleton). However, using a lock is inherently expensive; because, threads must wait in line to access the locked code—that's the whole point of a lock.
public sealed class Singleton { private static Singleton singleton = null; private static readonly object singletonLock = new object(); private Singleton() {} public static Singleton GetInstance() { lock (singletonLock) { if (singleton == null) { singleton = new Singleton(); } return singleton ; } } }
You will see so-called "double-check locking" examples like this:
if (singleton == null) { lock (singletonLock) { if (singleton == null) { singleton = new Singleton(); } } }
It is an attempt to avoid having to lock on every access; but, it simply doesn't work properly in all cases.
Thread-safe Singleton version 3
This implementation uses an inner class to make the .NET instantiation fully lazy. Only GetInstance() will trigger the type initializer; so, this version is just as lazy as the classic version. And, it will perform as quckly as any other version.
public sealed class Singleton { private Singleton() {} public static Singleton GetInstance() { return NestedSingleton.singleton; } class NestedSingleton { internal static readonly Singleton singleton = new Singleton(); static NestedSingleton() {} } }
- The
singletonvariable is declaredinternalso the outer class can access it.
- The
|

