In my previous article, you might have seen the basic example of the Singleton Pattern in C# that was not thread safe. In this article, we will see how to make it thread safe.
Thread Safe Singleton class using early binding
- namespace PrashantBlogs.SingletonPattern
- {
-
-
-
-
- sealed class Singleton
- {
-
- private static Singleton _instance = new Singleton();
-
-
-
-
- private Singleton()
- {
- }
-
-
- static internal Singleton Instance()
- {
-
-
- return _instance;
- }
- }
- }
The preceding class has a static method that returns the instance of the Singleton class that is already initialized during its declaration as in the following.
- private static Singleton _instance = new Singleton();
It ensures that the instance of the Singleton class is already created and the static method is an instance that just returns it whenever a call to this method is made by the calling applications.
Thread Safe Singleton class using lock
- using System;
- namespace PrashantBlogs.SingletonPattern
- {
- sealed class Singleton
- {
- private static readonly object _lockObj = new object();
- private static Singleton _instance = null;
- private Singleton()
- {
- }
- static internal Singleton Instance()
- {
- lock (_lockObj)
- {
- if (_instance == null)
- {
- _instance = new Singleton();
- Console.WriteLine("Created the instance of Singleton class");
- }
- else
- {
- Console.WriteLine("Returning already created instance of Singleton class");
- }
- return _instance;
- }
- }
- }
- }
The preceding class shows how the thread-safe singleton pattern can be implemented using the lock. The following line helps us to create an object that is used further to lock a critical section.
- private static readonly object _lockObj = new object();
We have declared the Singleton instance variable in such a way that it can be initialized lazily when we actually request the instance of the Singleton class.
- private static Singleton _instance = null;
The lock block forms a critical section to ensure that only one thread can enter it at a time and other threads must wait until the already entered thread comes out of this section so it's completely thread safe. The first thread will create the instance of the Singleton class and all other threads will share the same instance. However, if you observe it carefully, a lock is acquired every time the instance is requested that will hit the performance.
Thread Safe Singleton class using double-check locking
- using System;
- namespace PrashantBlogs.SingletonPattern
- {
- sealed class Singleton
- {
- private static readonly object _lockObj = new object();
- private static volatile Singleton _instance;
- private Singleton()
- {
- }
- static internal Singleton Instance()
- {
- if (_instance == null)
- {
- lock(_lockObj)
- {
- if(_instance == null)
- {
- _instance = new Singleton();
- Console.WriteLine("Created the instance of Singleton class");
- }
- else
- {
- Console.WriteLine("Returning already created instance of Singleton class");
- }
- }
- }
- return _instance;
- }
- }
- }
The preceding Singleton class implements thread safety without the necessity of taking out a lock every time. It also solves the concurrency problems. It also allows you to delay instantiation until the object is first accessed. In practice, an application rarely requires such type of implementation. As in most cases, a static initialization approach is sufficient.
To test the preceding classes, you can use the following code snippet that is a main program written for creating a single instance using the Singleton Pattern.
Program.cs
- using System;
- namespace PrashantBlogs.SingletonPattern
- {
- class Program
- {
- static void Main(string[] args)
- {
- Console.WriteLine("Attempting to get the Singleton class instance");
- Console.WriteLine("The instance name is obj1");
- Singleton obj1 = Singleton.Instance();
- Console.WriteLine();
- Console.WriteLine("This is the second attempt to get the Singleton instance");
- Console.WriteLine("The instance name is obj2");
- Singleton obj2 = Singleton.Instance();
- Console.WriteLine();
- Console.WriteLine("This is the third attempt to get the Singleton instance");
- Console.WriteLine("The instance name is obj3");
- Singleton obj3 = Singleton.Instance();
- Console.WriteLine();
- Console.WriteLine("Comparing the three instances, obj1 == obj2 == obj3");
- if (obj1 == obj2 && obj1 == obj3 && obj2 == obj3)
- {
- Console.WriteLine("obj1, obj2 and obj3 are sharing the same instance");
- }
- Console.ReadKey();
- }
- }
- }
Output
Your feedback will help me in further improvements context wise so please feel free to post it/them.