Click here to Skip to main content
15,867,594 members
Articles / Web Development / ASP.NET

Singleton Design Pattern: The Pragmatic Approach - Part II

Rate me:
Please Sign up or sign in to vote.
3.34/5 (12 votes)
24 Mar 2018CPOL14 min read 15.8K   114   21   9
Let us not mug up definitions. Embrace a design pattern through an evolution. Only the hurdles faced while solving real world problems will take you closer to a design pattern, not your next technical interview.

Introduction

Alright! So in the previous article here, we understood some nuances, concerns and considerations of why we need Singleton and we also tried one way to achieve it through static keyword which had few loop holes. Let’s try some things ourselves to overcome those loopholes so that we have full control on the Singleton instance of our class.

I want to stress it again that you please read the first part of this article to get a complete and thorough context of the talk.

A Note About Source Code

The source code for this blog is also available under my account on GitHub here. Please feel free to leave your pull requests in case you think I can improve it in any way.

A Basic Attempt to Ensure Singleton Instance of a Class

Let’s try to evolve further. Stop and think a bit. Scribble it on a piece of paper as to what all you might require to give a guarantee that only one instance of a class gets created in code and the moment anybody tries to create the second instance, then may be it disallows it and throws some illegal operation exception or may be returns the same instance that you had created in the first go.

Here is what I tried in a step by step manner. Try to match it with the notes you just scribbled and see if you were thinking on the same lines:

  1. One thing is sure that if my constructor is public, I can’t stop my colleagues from using the new keyword to instantiate the class. So, the very first thing that I did was to apply private access specifier to the constructor method. Cool!
    C#
    public class Logger
    {
            private string LogFileDirectory = "";
            //private constructor. Now nobody dares to create my instance through new keyword.
            private Logger()
            {
                LogFileDirectory = @"C:\ProgramData\StudentInfo\";
            }
         //other code removed for brevity
    }
  2. But now the next problem arises. The moment I applied the private access specifier, the language compiler will bar even me (an unbiased compiler) from creating the instance. Huh! But we’ve to create an instance. At least one instance. Isn’t it? I figured out a way. There is still a place left on this planet which can invoke that private constructor through new operator to get an instance. That place is inside the class itself. Now since instance is not possible, hence calling instance methods will not be possible so I introduced a static method GetInstance on the class so that I can call something from outside. GetInstance does the trick of creating class instance even though constructor is private. Here we go:
    C#
    public class Logger
    {
         private string LogFileDirectory = "";
    
            //private constructor. Now nobody dares to create my instance.
            private Logger()
            {
                LogFileDirectory = @"C:\ProgramData\StudentInfo\";
            }
            public static Logger GetInstance()
            {
                return new Logger();
            }
          //other code removed for brevity
    }

    Hey wait! Something is still wrong here. We wanted to have one instance but we also wanted a maximum of one instance as well. Now we’ve overcome that private constructor problem but will the call to GetInstance method not create a new instance every time? It will.

  3. So, to make it a truly singleton implementation, I introduced a private static reference to keep a check if an instance already exists. Have a look at a basic complete implementation:

    C#
    public class Logger
    {
        //reference which points to singleton object.       
        private static Logger singleton;
            private string LogFileDirectory = "";
            private Logger()
            {
                LogFileDirectory = @"C:\ProgramData\StudentInfo\";
            }
    
            public static Logger GetInstance()
            {
                //initialize singleton only once when it is found null for the first time.
                if (singleton == null)
                {
                    singleton = new Logger();
                }
    
                return singleton;
            }
            //other code has been removed for brevity
    }

    Nice. Finally, we reached a stage where we have a truly singleton class. Either there will be zero or at maximum 1 instance. Now, the calling semantics from the consumers will also change as shown below as we can’t use new keyword anymore to get an instance of the Logger class. Now we need to call the GetInstance method instead at the call site as shown below:

    C#
    public DataTable GetStudentData()
    {
                var studentInfo = new DataTable();
                //logger class instantiation with GetInstance
                var logger = Logger.GetInstance();
    
                try
                {
                    logger.Log("Method GetStudentData entered.", LogLevel.Trace);
                    var sqlConnection = GetDbConnection();
                    logger.Log("Connection established with database.", LogLevel.Trace);
    
                    SqlDataAdapter sqlDataAdapter = new SqlDataAdapter
                                         ("SELECT * FROM Student", sqlConnection);
                    sqlDataAdapter.Fill(studentInfo);
                    //no worries any more. It is the same instance.
                    //logger2 is just a reference to the same singleton object
                    var logger2 = Logger.GetInstance(); 
                    logger2.Log("Data obtained from database.", LogLevel.Trace);
                    logger2.Log("Number of students fetched." + 
                                      studentInfo.Rows.Count, LogLevel.Information);
                }
                catch (Exception ex)
                {
                            //logger3 is again just a reference to the same singleton object
                      var logger3 = Logger.GetInstance();
                    logger3.Log("An error occurred in GetStudentData method. 
                    Exception details are - " + ex.Message, LogLevel.Error);            }
                logger.Log("Method GetStudentData Exited.", LogLevel.Trace);
                return studentInfo;
    }

    Everything looks cool and sorted. Isn’t it? We wanted to ensure singleton instance for our class and we achieved it. Really? No, gentlemen. It is a wild world out there and real-world enterprise applications aren’t that simple as I’ve mentioned so far.

    Real world applications employ a concept called multi-threading where you can kick start more than one thread to get your tasks done faster as threads run in parallel using various CPU cycle switching algorithms e.g. round-robin. So, all that lovely code to create singleton instance can come crashing if your application has more than one thread. Let’s see how. We’ve more problems to overcome.

We Aren't Truly Singleton Yet: Preparing Yourself for the Nuances of Multithreaded World

So first let’s understand where things can go wrong in multi-threaded world in the implementation we’ve done so far. Consider the below portion of code which the soul of our singleton implementation. Line numbers are just for reference.

C++
public static Logger GetInstance()
{
        //initialize singleton only once when it is found null for the first time.
  1.    if (singleton == null)
  2.    {
  3.       singleton = new Logger();
  4.    }

        return singleton
}

Let’s say you’ve got two threads in all in your systems and both intend to get the singleton instance of the Logger class. Let’s call them T1 and T2.

Code executed by T1 and T2:

C#
public DataTable GetStudentData()
{
    var logger = Logger.GetInstance();
    //other code
}

Let’s say your operating system is following a round robin algorithm for context switching inside CPU and both threads have got equal priority.

Now we have just started the application. Let’s say the code running on T1 wanted to log something so it required the singleton instance and it is executing GetInstance method. So, it executed line # 1. It learned that singleton reference is currently null. Cool. Let’s move further.

But, the moment it was about to execute line # 2 and 3 context switching happened and control went to thread T2. We’re assuming a single core CPU here. T2 also wants to log something so it goes ahead and tries to execute GetInstance method. Incidentally, it turned out to be fast and it was able to execute the entire method in single CPU cycle so it was able to instantiate “Singleton” and then context switching happened.

Now T1 loaded its older context and realized that I have to start at line # 2. It remembered that Singleton wasn’t initialized, so it goes ahead and executes line # 2,3 & 4. Whoooaaa. We just instantiated our Singleton twice and our implementation stands broken ☹.

So, we require a fix here. The main problem here is the multi-threading world. Code line # 1, 2, 3 and 4 are critical pieces of code from threading stand-point. I need a mechanism so that if one thread is executing them, then the other thread should just wait before line # 1 else he might get stale/wrong information about instances. So I need to safeguard my code line # 1,2,3 & 4 against several threads running them simultaneously.

This thread synchronization is possible in C# using lock keyword . Let’s change the implementation to fix the problem (refer to SingletonWithLockingTextLogger.cs):

C#
public class Logger
{
        //reference which points to singleton object.
        private static volatile Logger singleton;
        //object used for creating locks for thread synchronization
        private static object syncRoot = new Object();

        public static Logger GetInstance()
        {
     1.      lock (syncRoot)
     2.      {
                //initialize singleton only once when it is found null for the first time.
     3.         if (singleton == null)
     4.             singleton = new Logger();
     5.      }
     6.      return singleton;
        }
//other code has been removed for brevity
}

We’ve used an object instance on which lock is applied. .NET guarantees that only thread at a time can obtain lock on the synchronization block of an object. Now the moment T1 executed line # 1, no matter how many CPU cycles come and go, T2 will have to wait before line # 1 until and unless T1 has reached line # 6. The moment T1 goes outside the locked code block, then T2 will enter and it will see the correct and up-to-date information that singleton is already initialized.

Finally, we’ve reached an implementation of creating singleton instances which is of real use and can be put to production deployment of an enterprise software.

Now we’ve got the functional correctness of Singleton instances. But you must have heard your manager or interviewer saying this common phrase – “Make this program run faster”. Of course, speed is everything in the world of computers. If two programs are achieving the same objective but the winner is the one which can do it in less time comparatively.

Can Our Application Perform Better?

Unknowingly, we’ve done something which has a performance cost. Let’s revisit the crucial piece of code. The point here is that anyone who wants a Logger class instance will invoke GetInstance method and he must acquire the lock. We’ve been following web application where we were trying to implement the singleton for all the logging needs of a single web call. Imagine an application like winForm desktop application or a Windows service application which keeps running 24*7. This method might get called a million times every day from various components of your distributed application as your application process remains alive for weeks and months. Correct?

So essentially your application code acquires lock a million times to get a singleton instance. Isn’t it? Your application acquires lock a million times which is a costly affair in terms of performance and memory. Always know this fact that locks are a costly data structure.

Now take a pause and think. Do you really require to take that lock a million times?

The answer is no. It is all about the conflict which can happen when instantiation hasn’t happened for the first time only. Once an instance has got created, nobody gives a damn about the lock as every subsequent execution will have to simply return the singleton instance which is already created.

C#
public static Logger GetInstance()
{
    lock (syncRoot)
    {
        //initialize singleton only once when it is found null for the first time.
        if (singleton == null)
            singleton = new Logger();
     }
     return singleton;
}

How can we overcome this performance issue which can be bad for my program as it grows? Let’s move on to next section to see the resolution.

Note: If you are running a single user and single threaded console application containing few scores of log statements on a 32 GB RAM server, then this performance improvement might not seem that worthy. For every other application in this world, you must know this.

Making Your Programs Run Faster in Multithreaded World: Double Checked Locking

So, we understand the fact that locking is relevant only till the time our singleton instance isn’t instantiated. Once the singleton is instantiated, we can simply check for it being non-null and return the non-null instance. So, what you can do is to add an additional null checking statement surrounding the locked code block as null checking is an extremely light weight operation when compared to taking a lock for thread-synchronization:

C#
public static Logger GetInstance()
{
             //once the singleton instance is instantiated all calls will jump to return
            //statement after this line instead of going inside the if statement and acquiring the lock  
            if (singleton == null)
            {
          //if singleton isn’t initialized so initialization piece code
                //is thread synchronized so that only one instance gets created.
                lock (syncRoot)
                {
                    //initialize singleton only once when it is found null for the first time.
                    if (singleton == null)
                        singleton = new Logger();
                }
            }
            return singleton;
}

This is Not the Only Way to Write Singletons

There are some other variations of writing this piece of code which we were finally able to evolve over last few sections. See, at the end of the day, what we want is that our below two objectives should be met irrespective of what code we write:

  1. One and only one instance of the class should get created.
  2. The above statement should be always true. It should be true whether my application is single threaded or multi-threaded.

To ensure that the first objective wasn’t that difficult, but the second objective requires us to understand some complex concepts of multi-threading world. To ensure thread safety or thread synchronization in multi-threaded application, every language provides many built-in features which you can leverage to get the same effect for which we wrote code. It is like reinventing the wheel if we aren’t using those built-in language features.

So, in place of doing locking, double checking and things like that, you can use these smart language features, so your lines of code get reduced but intent remains the same. You also end up having same code written differently which we can call a variation. Here are few possible variations:

  1. Simply use in-line static initializer (Eager instantiation): So here, we use CLR’s capability which gives us the guarantee that any static initialization will happen only once in a thread safe manner. Please note that class has been decorated with sealed keyword to avoid inheritance. This can be a downside in this implementation as we originally thought that our Singleton class should be able to participate in inheritance in case we’re changing an already existing application.
    C#
    public sealed class Logger
    {
            //CLR guarantees that this line of code runs in a thread-safe manner
            private static readonly Logger singleton = new Logger();
        //other code has been removed for brevity
    
            public static Logger GetInstance()
            {
                //simply return the already initialized instance
                return singleton;
            }
            //other code has been removed for brevity
    }
  2. Lazy Instantiation by introducing static constructor: In this variation, we introduce an explicit static constructor in Logger class. Having an explicit static constructor instructs the compiler to not decorate the class with beforefieldinit keyword (while creating MSIL code) which causes eager instantiation of static classes.
    C#
    public sealed class Logger
    {
            //CLR guarantees that this line of code runs in a thread-safe manner
            private static readonly Logger singleton = new Logger();
    
        private string LogFileDirectory = "";
    
            // Explicit static constructor
            static Logger()
            {
    
            }
    
            //private constructor. Now nobody dares to create my instance.
            private Logger()
            {
                LogFileDirectory = @"C:\ProgramData\StudentInfo\";
            }
    
            public static Logger GetInstance()
            {
                //simply return the already initialized instance
                return singleton;
            }
    }

    So, we figured out a way for lazy instantiation in statics also. 😊

  3. .NET 4 Lazy class-based implementation: A very sweet and short implementation in .NET v4.0 using built-in Lazy class in framework class library (FCL):
    C#
    public sealed class Logger
    {
        static readonly Lazy<Logger> lazy = new Lazy<Logger>(() => new Logger());
        private Logger() { }
    
        public static Logger Instance => lazy.Value;
    }

    It requires you to have at least .NET Framework 4 and C# 6.0 (VS 2015) or higher for the above code to work.

Singletons are Considered a Bad Design Choice

I know you will be upset with me that after such a mammoth article, I’m telling you this that Singletons are a bad design choice. This is not always the case, but it is good to know few downsides which are as below:

  1. They introduce a global context variable which is considered a bad design choice.
  2. Implementation is not transparent to the consumer. Instead of using “new” keyword to instantiate a singleton class, now you use a special method (GetInstance in my implementation) to get the Singleton instance. So when you refactor your existing code and convert one of your classes to Singleton, then you will have to change code at all the call sites. By the way, this is not a heavy price to pay to make your program behave functionally correct and become better performing.

Concluding Thoughts: You Need a Definition to Tell in Your Next Interview

To cheer you up, I look at Singletons as “Controlled Globals”. The idea is that don’t try to use Singletons just because you want to make something global. When you have a dire need for creating and managing singletons, then only you should think of singletons. When Singletons help ensure the functional correctness of your program and make it better performing, then you should be confident of using it. Some very famous use cases which are great fit for Singletons are loggers, print spoolers, database connections, etc.

Never think of patterns first. Always evolve!

Never try to mug up the definition of any design pattern. Look at use cases around yourself. Look at products you are working upon. Look at frameworks or libraries that you use in your day to day life. “Application” class in ASP.NET is singleton. Why?

If you a tell a child to learn the word “round”, he will struggle. The moment you show him a ball and tell him that it is round, then he learns.

I could have provided the formal definition of this pattern in the beginning itself, but I left it for the end. Patterns are not something you should begin with while solving a problem. Solve a problem first. Solve one more problem. May be solve one more. And then you realize that several problems are getting solved repeatedly in the same way. Then it becomes a pattern. A design pattern. Design pattern terminology is a jargon used in programmer’s community. It brings succinctness in your discussions.

I'm giving the definition here just for the sake of completeness of the article from Gang of Four (GOF) directory:

C#
"The Singleton Pattern ensures a class has only one

instance, and provides a global point of access to it."

Final Tip

Tell me and I will forget.
Teach me and I will remember.
Involve me and I will learn.

Involvement comes with coding and discussion. Try to see if Singleton Design pattern is a good fit in any of the use cases when you start your next green field project or do some refactoring or try to solve a performance issue. Don't forget to talk about whatever you have learned here with your colleagues, friends and acquaintances. You can learn it only when you get involved. There isn't a workaround for it.

Design patterns aren’t about definitions, they are about patterns which evolve out of use cases.

Points of Interest

  • Did you notice the volatile keyword in “We aren’t truly singleton yet: Preparing yourself for the nuances of multithreaded world” section? What is the significance of using it in multi-threaded environment. What advantages does give?
  • You saw other possible implementations of Singleton Design Pattern in “This is not the only way to write singletons” section. Can you explore their advantages and disadvantages?
  • Explore the case of Singleton Design Pattern where it has been used in .NET Framework here. A cheat-sheet of various design patterns used in .NET Framework class library is here.
  • Evaluate other places where you can find it useful to apply singleton design pattern. I’m quoting some examples below. Ponder on the reasons as to why it makes sense to use the design pattern in below cases and what problems I might face if I don’t use it?
  • Maintaining configurations from your application configuration file.
  • Communication of the chat windows in a chatting application using a socket – Would you want to open a new socket for every new chat widow? Is creating socket a heavy operation?

History

  • 08 March, 2018: First draft
  • 19th March, 2018: Added a new section "A Note About Source Code".

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)


Written By
Software Developer 1E
India India
My professional details will hardly make a difference to you if you have come to know about me after reading my lengthy article but my intent will make a difference for sure. Why I write articles is for the sole motto which I got to know pretty late in my career. My motto is that there is ONLY one way of enhancing knowledge and that way is sharing. Blogs, articles and white papers are nothing but a form of knowledge sharing. It also helps you creating a backup copy of the knowledge you have in your brain and nerves. This backup copy will surely outlive your life as this knowledge gets transferred to uncountable people through sharing. So start contributing now! Always remember my favorite quote - "Knowledge is the ONLY antidote to all fears. Start reading a new book today". Don't seek Nirvana. Seek knowledge. Nirvana and knowledge are synonymous.

Comments and Discussions

 
BugDouble Checked Locking problem Pin
Frank Malcolm28-Mar-18 11:33
Frank Malcolm28-Mar-18 11:33 
Suggestiongood and bad singletons Pin
sx200827-Mar-18 10:17
sx200827-Mar-18 10:17 
QuestionGood Job Pin
raju dasa25-Mar-18 7:11
raju dasa25-Mar-18 7:11 
AnswerRe: Good Job Pin
Rasik Bihari Tiwari26-Mar-18 2:38
professionalRasik Bihari Tiwari26-Mar-18 2:38 
PraiseI liked both Parts of your presentation Pin
asiwel13-Mar-18 18:50
professionalasiwel13-Mar-18 18:50 
GeneralRe: I liked both Parts of your presentation Pin
Rasik Bihari Tiwari13-Mar-18 20:18
professionalRasik Bihari Tiwari13-Mar-18 20:18 
QuestionSingleton Pin
geoyar12-Mar-18 14:56
professionalgeoyar12-Mar-18 14:56 
AnswerRe: Singleton Pin
Rasik Bihari Tiwari13-Mar-18 21:26
professionalRasik Bihari Tiwari13-Mar-18 21:26 
SuggestionSingleton Pin
sbsen200012-Mar-18 13:17
sbsen200012-Mar-18 13:17 

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Praise Praise    Rant Rant    Admin Admin   

Use Ctrl+Left/Right to switch messages, Ctrl+Up/Down to switch threads, Ctrl+Shift+Left/Right to switch pages.