Pages

Wednesday, June 1, 2016

Thread-safe Singleton Pattern (Multi-threaded Singleton Pattern)

In my previous post Singleton Pattern, I explained various ways to implement singleton pattern but those were not
thread-safe. In multi-threaded environment it’s quite possible that multiple
thread enters into Instance property at the same time and more than one object of
Singleton class may be created. In this article I'll show you various ways to
create thread-safe singleton pattern.

//This static property responsible for
creation of instance of this class.

publicstaticSingleton
Instance

{

get

{

lock (objLock)

{

if
(instance == null)

{

Console.WriteLine("New
Instance created");

instance = newSingleton();

}

}

return instance;

}

}

publicvoid
Display()

{

Console.WriteLine("Display
Method called from Singleton Class...");

}

}

classProgram

{

staticvoid Main(string[] args)

{

//array of threads

Thread[] thrd = new
Thread[4];

for (int i = 0;
i < 4; i++)

{

thrd[i] = new Thread(new
ThreadStart(() =>

{

Singleton
singleObject = Singleton.Instance;

singleObject.Display();

}));

thrd[i].Start();

}

Console.Read();

}

}

Output –

You can see “New Instance created” message displayed only once since new instance created only once across multiple thread calls. The above example
allows only single thread to enter instance creation code when no instance created. The lock block allows only single thread to enter and create
an instance.

In this approach lock is acquired every time the instance of
class is requested by user so it will impact performance. To avoid an exclusive locking in every call of Instance property, you can use double-check locking approach.

//This static method responsible for
creation of instance of this class.

publicstaticSingleton
Instance

{

get

{

if (instance == null)

{

lock
(objLock)

{

if
(instance == null)

{

Console.WriteLine("New
Instance created");

instance = newSingleton();

}

}

}

return instance;

}

}

publicvoid
Display()

{

Console.WriteLine("Display
Method called from Singleton Class...");

}

}

classProgram

{

staticvoid Main(string[] args)

{

//array of threads

Thread[] thrd
= newThread[4];

for (int i = 0;
i < 4; i++)

{

thrd[i] = newThread(newThreadStart(()
=>

{

Singleton
singleObject = Singleton.Instance;

singleObject.Display();

}));

thrd[i].Start();

}

Console.Read();

}

}

Output -

Same as above example.

In double-check locking approach, Only first thread acquires lock and creates object while other threads will not be able to acquire lock since I added check before lock statement. So this way we can reduce acquiring lock for each and every thread who calls Instance property and improve performance. I have declared instance variable as volatile so it will read from physical memory every time instead of thread's internal cache.

Thread-safe without using locks via static initialization

As per above example, we can implement thread-safe singleton
pattern using locks but there is another way to create thread-safe singleton
pattern without using locks but via static member initialization. This approach
does not support lazy initialization of an object like above example does.

Code –

namespace
ThreadSafeSingleton

{

publicsealedclassSingleton

{

privatestaticSingleton
instance = newSingleton();

private
Singleton() { }

publicstaticSingleton
Instance

{

get

{

return
instance;

}

}

publicvoid Display()

{

Console.WriteLine("Display Method called from Singleton Class...");

}

}

}

namespace
ThreadSafeSingleton

{

classProgram

{

staticvoid Main(string[]
args)

{

//array
of threads

Thread[]
thrd = newThread[4];

for
(int i = 0; i < 4; i++)

{

thrd[i] = newThread(newThreadStart(()
=>

{

Singleton
singleObject = Singleton.Instance;

singleObject.Display();

}));

thrd[i].Start();

}

Console.Read();

}

}

}

Output –

With this approach, new instance is created only once when any static member or method of this class is referenced. This uses static variable initializer approach and CLR takes care of it. In this approach you have very less control over the
creation of the object.