BLOG
15 December 2021

The Singleton Design Pattern: the 'Easy' Pattern

tech

This is my second article in the “Design Patterns” series. The first article was about the strategy pattern and can be found at: this link. In this article post we will be focusing on a most popular design pattern ever invented, the singleton design pattern. 

The popularity of the singleton design may be its very undoing. Because many consider the single pattern to be trivial and not worth fully understanding. I strongly disagree with that approach, as the singleton pattern is one of the most effective in its public class. On the other hand, the singleton design pattern is an often lazy, or even badly-deployed created instance.

So, whether you're a fan of the singleton class, or a diehard enemy of this instance of the class - then I created this article for you. Enjoy access to a thread of knowledge with global proportions!

What is the singleton design pattern?

A singleton pattern is the most popular design pattern in the world, and is a global point of access. And, I'm not using this term lightly, either!

This is the first design pattern to get familiar with. A singleton design pattern is also the first one we are using in terms of design patterns. Any kind of a staple question when it comes to job interviews will use phrases like: "class singleton private static examples", "apply the singleton pattern", "singleton class this, and singleton instance that..." 

The goal of the singleton pattern is simple. It ensures that we have only a single instance of a class throw-out of an entire application.

Why would you start with a singleton design pattern?

The valid question at this point is, or at least should be “Why didn’t we start with the singleton pattern”? The reason for that is that due to its “simplicity” the important details are often overlooked. This may cause poor performance or unexpected behaviours in some specific situations. That is why, my goal is to present the right way of creating a singleton pattern.  

The problem with class singleton private static 

As we already know. Design patterns are here to solve our problems. What is the problem with the singleton class, you might ask? Very good question. In this case, the answer is very simple. We want to make sure that a solution has only one instance of a specific class. This is a very common scenario. Let’s imagine main configuration class or an application user preferences class. 

We probably want only one instance of a configuration or a user. This is an excellent case for a singleton. This parent was created to ensure that such a class would be instantiated only once. Sounds easy? Good. I will try to change this opinion.  

The code presented in this post can be found at: this link. The projects in the solution correspond with patterns names.  

Naïve approaches to the singleton class

Let’s start simple. The first example of singleton that we will focus on is probably the most popular.  It can be found at SingletonPattern.Implementations.NaiveSingleton.cs 

public sealed class NaiveSingleton ,[object Object],{ ,[object Object],   private static NaiveSingleton _instance; ,[object Object],     ,[object Object],   private NaiveSingleton() ,[object Object],   { ,[object Object],        Console.WriteLine("Naive Singleton Constructor"); ,[object Object],   } ,[object Object], ,[object Object],   public static Naive Singleton Instance ,[object Object],   { ,[object Object],       get ,[object Object],       { ,[object Object],           Console.WriteLine("Instance");  ,[object Object],          return _instance ??= new NaiveSingleton(); ,[object Object],       } ,[object Object],   }  ,[object Object],}

The singleton class itself is rather straightforward and is a perfect example of a naïve singleton design pattern. 

Singleton class: element one 

The first element we should focus on, is a private constructor. We don’t want anyone, except its class code, to be able to call him. This kind of private constructor usually has some logic in them. It this case we don’t need him to do anything, except of logging an info, that this constructor was called. This is the first essential part of a constructor. 

Singleton class: element two 

The second one is storing an instance of a class inside itself. This also should be done with a private field. So that, just like with the constructor, only the containing class may use it. 

Singleton class: element three 

The third, and last part of a singleton design pattern, is a way to aquire objects. In a way that will ensure, that there is only a single instance of it. It is being done by an Instance property. It only has a getter. It returns the Naive Singleton held in _instance field. If it is first usage of Instance property _instance is being set as a new NaiveSingleton. In all the future usages Instance will be returning an object created during the first usage.  

Using these three simple elements, we managed the create a class. That can be instantiated only by a public property. This property ensures that only one instance of a class is in use. Or does it? Let’s create a basic test scenario. To do it, we need to run an appropriate method. 

1 2 3 4 5 6 7 8 9 10 11 12 public static void NaiveSingletonUsaga() { var inst1 = NaiveSingleton.Instance; var inst2 = NaiveSingleton.Instance; var inst3 = NaiveSingleton.Instance; } We can do that by calling this method in the main method. static void Main(string[] args) { Casses.NaiveSingletonUsaga(); }

Results should look like so. 

First use of the NaiveSingleton.Instance triggers the constructor. That is indicated by the entry in a console. Second and third use only read the instance. They don’t use a constructor. This proves that the object is being created only the first time we reference it.  

So... the singleton pattern works, or not? 

Well…. unfortunately, it doesn’t. To be fair, it works sometimes. But that is not good enough. That is why I started with a statement that the singleton design pattern looks simple on the surface, but it can get tricky. The trick is, to put parallelism into the mix. The code that is going to break the naive approach is inside the method 'Naive Singleton Parallel Problem'. The idea behind this test is simple. We are going to run a parallel loop and see what happens.

The results may vary from machine to machine or even run to run. But the problem will occur. What is the problem? You may ask. The problem is that the constructor was executed more than once. Common sense tells us that it should not happen. Should it? Well … yes and no. The problem is with the if statement inside of singleton class. The ??= operator has a hidden if logic in it. It will execute the constructor, if element on its left side is null. In our multiple thread example, the objects reach this point in our code. Before the objects have a chance to re-create. This kind of a problem occurs when the code is not thread safe. So, it gets used by many thread amounts in a way that compromises its goal. 

To prevent such unwanted object behaviour, and thread safety, we need to make sure that the instance resource is thread exclusive. It means, that only one thread can use it at a given moment. This technique is called lock or padlock. To use this technique in our solution we need just a slight code modification.  

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 public sealed class ThreadSafeSingleton { private static ThreadSafeSingleton _instance; private static readonly object padlock = new object(); private ThreadSafeSingleton() { Console.WriteLine("Thread Safe Singleton Constructor"); } public static ThreadSafeSingleton Instance { get { Console.WriteLine("Instance"); if (_instance == null) { lock (padlock) { _instance ??= new ThreadSafeSingleton(); } } return _instance; } } }

This slight modification is focused on a padlock object. It is just a simple object, because we are not going to use any of its features. We are going to be using it as a padlock of sort. When thread calls the Instance property and the _instance is null, we will lock the padlock object, before calling the constructor. 

Thanks to this technique the other threads won’t be able get in this part of code, until padlock object gets released. And that is all to it. This simple trick makes singleton pattern thread safe. You don’t need to take my word for it. All we need to do is call ThreadSafeSingletParallelUsage method and see the results.

Great success. The constructor got called in only a single instance. This is because the part of code that was invoking it was thread safe. When first thread was done with it. The other once step in, but the _instance was already under initialization by that time. That is why the double instance null check. One was in the 'if' and second one in ??=.

So it is fast? 

The code we have written works. And it is going to work in every single situation. But is it the best we can do? As is turns out it is not. We can make our code much faster. This improvement can be achieved by using build via a lazy initialization, or 'Lazy<>' type. Before talking about new code, lets have a little demo. I changed our main code to something like this.  

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 static void Main(string[] args) { var watch1 = new Stopwatch(); var watch2 = new Stopwatch(); watch1.Start(); for (int i = 0; i < 1000; i++) { Casses.ThreadSafeSingletParallelUsage(); } watch1.Stop(); watch2.Start(); for (int i = 0; i < 1000; i++) { Casses.UltimateSingletonParallelUsage(); } watch2.Stop(); Console.WriteLine($"ThreadSafeSingletParallelUsage x 1 000 took: {watch1.ElapsedMilliseconds}"); Console.WriteLine($"UltimateSingletonParallelUsage x 1 000 took: {watch2.ElapsedMilliseconds}"); }

     

The goal is to compare the speed of our current implementation with the one I’m going to show in a minute. The code runs each test method a thousand times and shows us how long each took.  

We can clearly see that the new approach is approximately two times faster. The results may vary based on the number on object instance reference. Nevertheless, the code with the Lazy<> component will always be faster. We gave it such an introduction, so lets see the code. 

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 public sealed class UltimateSingleton { private static readonly Lazy _lazy = new Lazy(() => new UltimateSingleton()); public static UltimateSingleton Instance { get { Console.WriteLine("Instance"); return _lazy.Value; } } private UltimateSingleton() { Console.WriteLine("Ultimate Singleton Singleton Constructor"); } }

The code got changed quite a bit. The most significant change is the type of a private field. Now it is of type Lazy<T>. If you haven’t heard of Lazy<T>, it is an interesting type. It gives us build-in support for lazy, thread safe initialization. It is essentially tailor made for the singleton design pattern. 

All we need to provide, to use this type efficiently, is a generic type and a callback function. The method is to return an object of passed generic type. Thanks to the Lazy initialization type, we can hit two birds with the one stone. We are thread safe, and we are sure that the instance gets to create only during only the first initialization. This is widely believed to be a preferable implementation of a singleton pattern.  

Singleton design pattern: a crucial recap

It's a necessary evil, and like I said over the course of this article; the singleton pattern will provide a global point of access for those looking for a faster initialization into their next job.

And while the example problem and the singleton pattern itself looked trivial at the start, the devil was hidden in the details. We have seen that both: parallelism and performance can present a challenge. Luck would have it that we have managed to overcome them both.

Access my other design pattern article too!

I hope you enjoyed this kind of an approach to a topic. The singleton design pattern can be used to guard your code, from having many instances of a given class.  

What is your take on the singleton pattern? Let me know at karol.rogowski@softwarehut.com. Be sure to access my other article on the strategy pattern as well.

Read Tech Stories from Experienced Devs!


Author
Karol Rogowski
Head Of Engineering

Working in IT since 2009. Currently working as Head of Engineering at SoftwareHut and as an academic teacher at Białystok Technical University. Co-founder of meet.js Białystok. Book and articles author. Father, husband, huge H.P. Lovecraft fan and terrible poker player.