In this post I will demonstrate creating a singleton class in .Net that is thread safe. As usual the singleton class should have a private constructor as shown below so that any other class cannot “directly” create an instance of our singleton class.

Apart from that, I have created two global variables, one to hold the instance of the Singleton class and another one of type object which will be used later for synchronization between threads (more on that later).

[sourcecode language="csharp" padlinenumbers="true" light="true" wraplines="true"] static SingletonSample _instance; static readonly object _synchronizer = new object(); /// <summary> /// Private C'tor, so that this class cannot be instantited by other classes directly /// </summary> private SingletonSample() { } [/sourcecode]
Method 1 – Using synchronization lock

 

GetInstance is a public static method, this method is the only way by which any other class can get instance of the singleton class.

Below code snippet uses double check pattern before creating the instance of the singleton class. The first If statement, if true, will give the instance of Singleton class instantly. However, if _instance variable is null then program will enter a critical section (the lock statement) synchronized using a global object (_synchronizer). Inside the critical section, I am checking the _instance  variable again if it is null (double check), if it is, then we finally create the instance of the class.

One interesting point to note here is, I am not creating the instance directly and returning it back. I am first assigning the class reference into a temporary object of type SingletonClass (tempInstance) and then doing a  Volatile write on the actual instance variable (_instance).

 

 

[sourcecode language="csharp"] /// <summary> /// Method is responsible for providing instance of this class. /// </summary> /// <returns>Instance of SingleTonSample class</returns> public static SingletonSample GetInstance() { //If instance is available return that. if (_instance != null) return _instance; //Critical section. Ony one thread can enter at a time. lock(_synchronizer) { //Check again if the instance in null if(_instance == null) { SingletonSample tempInstance = new SingletonSample(); //Volatile write to make sure that _instance is //populated with tempInstance reference without any compiler optimization. Volatile.Write(ref _instance, tempInstance); } } return _instance; } [/sourcecode]

The problem with this approach is that if this method is used in a highly parallel system, the critical section will cause all threads to do nothing but wait for an instance of Singleton class to be created. However, this will happen only once when the first instance is getting created, but still, this is wasteful of the resources.

Below is another, more efficient way of creating a singleton which neither uses double check pattern nor it has a critical section for thread synchronization.

Method 2 – Without Critical Section or Double Check

Below method will allow multiple threads to create instance of Singleton class, but Interlocked class’s CompareExchange will make sure that only one instance is assigned to _instance variable. Objects created by other  threads and not assigned to _instance variable will soon become Orphan and  will be garbage collected at a later point of time by the CLR.

[sourcecode language="csharp"] /// <summary> /// Method is responsible for providing instance of this class without critical section /// or double check pattern. /// </summary> /// <returns>Instance of SingletonSample class</returns> public static SingletonSample GetInstance() { //If instance is available return that. if (_instance != null) return _instance; SingletonSample tempInstance = new SingletonSample(); /*Multiple threads will create singleton object, but only *one will be asssgined to _instance variable. Rest others will be *GC'd (Garbage Collected) later at some point of time. */ Interlocked.CompareExchange(ref _instance, tempInstance,null); return _instance; } [/sourcecode]

Both methods are thread safe. However, method 2 is marginally more efficient and lean as compared to the first one.