Click here to Skip to main content
15,868,009 members
Articles / Programming Languages / C++

Singleton Design Pattern and Thread Safety

Rate me:
Please Sign up or sign in to vote.
4.80/5 (29 votes)
26 Jul 2010CPOL5 min read 173.7K   33   36
Everything there is to know about Singletons and thread safety.

Introduction

Okay, you're not a newbie, you know all about singletons (only one object instantiation during program execution), but do you really know everything there is to know about Singletons and thread safety?

Background

Who is this article for?

  1. This article is for people who know about Singletons and want to learn about thread safety for Singletons.
  2. This article is mainly for discussing design and for C++,  though you are okay knowing any other OOPS language. Do note, managed languages have additional solutions.

This article is not for people who do not know:

  1. What thread safety means.
  2. Basic understanding of Singleton – you might still be okay if you know about static methods.
  3. Design gurus and experts; this article is targeted for intermediate developers and designers in the making.

The Singleton

Let’s write a quick pseudo code for Singletons:

C++
class MySingleton
{
public:
      static MySingleton * GetInstance()
      //static method that returns only instance of MySingletone
      {
            if (m_pOnlyOneInstance == NULL) // if not yet instantiated
            {
                  m_pOnlyOneInstance = new MySingleton();
                  //create one and only object
            }

            return m_pOnlyOneInstance;
      }

private:

      static MySingleton * m_pOnlyOneInstance;
      //holds one and only object of MySingleton

      MySingleton(); // private constructor

public:
      // MySingleton functionalities
      void foo();
      bool goo();
      int zoo();
};

Thread Safety Issue for Singletons

Remember, thread-safety issue for Singletons would occur only rarely, as follows (rarely, but still catastrophic! so you still need to design for it):

  1. No client code has called GetInstance() so far, and now two threads simultaneously call GetInstance(), and
  2. Context switch between the two calling threads happen on the exact line of code at:
  3. C#
    if (m_pOnlyOneInstance == NULL)

During further calls to GetInstance(), the MySingleton object is already created and would be returned. But it's still a serious issue, as we've instantiated MySingleton twice.

Have you made your Singleton Thread-Safe?

Note: Think about how you'll make your Singleton thread-safe, come up with solutions before proceeding further.

Solution 1

Easy, put a critical section to my Singleton method to make it thread-safe. Duh, is it gonna be that kinda article?

C++
MySingleton * GetInstance() //usage of critcal section makes it thread safe
{
      EnterCriticalSection();
      //other client threads, if any, now have to wait till current
      // thread leaves critical section. Forget semantics and
      // compilation for Crit Section, and treat this as pseudo-code
      if (m_pOnlyOneInstance == NULL)
      {
          m_pOnlyOneInstance = new MySingleton();
      }

      LeaveCriticalSection();

      return m_pOnlyOneInstance;
}
Deep Dive into Solution 1

Yup, this solution works, but think about it: critical section is a costly operation, and you're using it each and every time a client accesses GetInstance().

You've devised a solution that works and handles the rare but serious thread safety issue for singletons, but at the cost of doing an expensive critical section operation for all GetInstance() calls, slowing down client access every time!!

This is clearly unacceptable. Isn't there a better solution?

Solution 2

Okay hot shot, if critical section is expensive, let's give it the boot.

Let's call MySingleton::GetInstance() during program start-up, like in main() in C++, or CWinApp:InitInstance() in MFC, and so on an so forth.

I know there is only one thread executing during program start-up, so thread-safety issue does not even arise.

Design Principle: This kind of instantiation is called Eager Instantiation. That means, creating objects up-front, even before they are required or might be used.

Yup, this works. No critical section involved, so no costly operation for the general use-case when clients call GetInstance() every time.

Wait a minute, trying to pull a fast one, eh? I know about the basic OOAD Design Principle of Lazy\Late Instantiation, which means create an object only when required, not upfront. Aren't we breaking this design principle?

Heck, yes, we are! Let's plunge into this. But let's quickly define Late Instantiation before that:

Design Principle: Late Instantiation means creating an object when it is required to be used, not up-front.

We’ve covered two design principles of Early and Late Instantiation already!

Deep Dive into Solution 2 (keep your beach towels ready!)
  1. What if no client calls MyInstance() during program execution? Maybe the client ran a use-case this time that did not need MySingleton's usage. You've created an unnecessary object that's floating around during the entire program life-cycle doing nothing.
  2. While Early or Lazy Instantiation might not sound like a big deal, what if MySingleton is a memory-hogging class? What if MySingleton represents data stored on a file, or detailed info about a server? You're occupying lot of precious memory that might never potentially be used!
  3. Eager Instantiation is not all bad. If your Singleton is a basic class that is heavily used all across your program, then by all means, go for Eager Instantiation.

Lazy Instantiation is a principle, not a rule, and not necessarily always the default choice. Be either Eager or Lazy, depending on your design and domain needs! There is no good or bad, you have to choose what's best based on your program needs. Solution 2 (Eager Instantiation) is a pretty good, easy, clean solution for many projects, and is widely used.

But isn’t there a solution to address these short-comings of Solution 2? Yup, there is! Enter Solution 3.

Solution 3

We can achieve good performance and lazy instantiation for Singletons (which were the short-comings of Solutions 1 and 2 for the short-term memory loss readers out there).

You can achieve this by moving around code in Solution 1.

Do go back to Solution 1 and think about how this can be done before proceeding further.

Deep Dive into Solution 3
C++
MySingleton * GetInstance()
{
      if (m_pOnlyOneInstance == NULL)
      {
            EnterCriticalSection();
            if (m_pOnlyOneInstance == NULL)
            // Solution 1 and 2 gaps addressed by moving
            // critical section block and by re-doing this check!
            {
                  m_pOnlyOneInstance = new MySingleton();
            }
            LeaveCriticalSection();
      }
      return m_pOnlyOneInstance;
}

With Solution 3, you do not use a critical section every time a client calls GetInstance(), and we achieve Lazy Instantiation. The MySingleton object is created only when the client calls GetInstance().

Also, a Critical Section is used only during instantiation, and for handling the rare (but catastrophic!) thread-safety issue during instantiation and the race condition between two threads. We do not enter a critical section block every time the client calls GetInstance().

Congratulations! You’ve just learnt Double-Checked Locking, the formal name for Solution 3.

Summary

We’ve covered quite a bit of ground there with the simplest of Design Patterns: the Singleton. We’ve applied a mini-OO Analysis and Design on a small scale for our friend MySingleton. And, as a bonus, we learnt about Eager and Lazy Instantiation!

Remember, you can choose either Solution 2 or Solution 3 for your Singletons, based on your project need.

There is another pattern called Monostate with which you could achieve Singleton-like behavior, but it won’t please the purists out there.

The Monostate pattern is simply making all methods and data of your class static.

C++
class MonoState
{

public:
      // MonoState functionalities
      static void foo();
      static bool goo();
      static int zoo();

      MonoState() {} // constructor NEITHER private NOR static

private:

      static int MyData;
};

Clients of MonoState access it by creating an object and calling the static method. Since all methods and variables are static, data is shared across objects and we get a Singleton like behavior.

C++
MonoState newObject;
newObject.goo();

MonoState msObject;
msObject.zoo();

But note that what we get from MonoState is Singleton like behavior, because MonoState creates multiple objects but that which share the same data. Anyway, MonoState is theoretical, and for the sake of rounding up a complete article, you should always be going for the Singleton pattern, rather than MonoState.

License

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


Written By
India India
10 years of extensive experience in full life-cycle of S\W development inlcuding Requirements, Design, Prototyping, Implementation, Verification, Release, Sustenance.

Comments and Discussions

 
GeneralRe: How about this? [modified] Pin
imagiro26-Jul-10 21:39
imagiro26-Jul-10 21:39 
AnswerRe: How about this? Pin
Aescleal27-Jul-10 1:24
Aescleal27-Jul-10 1:24 
GeneralRe: How about this? Pin
imagiro27-Jul-10 1:52
imagiro27-Jul-10 1:52 
Generaldouble-check Pin
oceanchow26-Jul-10 14:17
oceanchow26-Jul-10 14:17 
GeneralRe: double-check Pin
Ananth.tm26-Jul-10 17:14
Ananth.tm26-Jul-10 17:14 
GeneralRe: double-check Pin
Aescleal27-Jul-10 1:20
Aescleal27-Jul-10 1:20 
QuestionStatic Constructor Pin
gururaaj26-Jul-10 8:03
gururaaj26-Jul-10 8:03 
AnswerRe: Static Constructor Pin
Justin Helsley26-Jul-10 9:19
Justin Helsley26-Jul-10 9:19 
GeneralRe: Static Constructor Pin
Ananth.tm26-Jul-10 17:09
Ananth.tm26-Jul-10 17:09 
AnswerRe: Static Constructor Pin
Aescleal27-Jul-10 1:18
Aescleal27-Jul-10 1:18 

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.