Singleton Design Pattern and Thread Safety

http://www.codeproject.com/Articles/96942/Singleton-Design-Pattern-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:

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:
    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?

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
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.

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.

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)

About the Author
Ananth.tm

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

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
经导师精心指导并认可、获 98 分的毕业设计项目!【项目资源】:微信小程序。【项目说明】:聚焦计算机相关专业毕设及实战操练,可作课程设计与期末大作业,含全部源码,能直用于毕设,经严格调试,运行有保障!【项目服务】:有任何使用上的问题,欢迎随时与博主沟通,博主会及时解答。 经导师精心指导并认可、获 98 分的毕业设计项目!【项目资源】:微信小程序。【项目说明】:聚焦计算机相关专业毕设及实战操练,可作课程设计与期末大作业,含全部源码,能直用于毕设,经严格调试,运行有保障!【项目服务】:有任何使用上的问题,欢迎随时与博主沟通,博主会及时解答。 经导师精心指导并认可、获 98 分的毕业设计项目!【项目资源】:微信小程序。【项目说明】:聚焦计算机相关专业毕设及实战操练,可作课程设计与期末大作业,含全部源码,能直用于毕设,经严格调试,运行有保障!【项目服务】:有任何使用上的问题,欢迎随时与博主沟通,博主会及时解答。 经导师精心指导并认可、获 98 分的毕业设计项目!【项目资源】:微信小程序。【项目说明】:聚焦计算机相关专业毕设及实战操练,可作课程设计与期末大作业,含全部源码,能直用于毕设,经严格调试,运行有保障!【项目服务】:有任何使用上的问题,欢迎随时与博主沟通,博主会及时解答。
经导师精心指导并认可、获 98 分的毕业设计项目!【项目资源】:微信小程序。【项目说明】:聚焦计算机相关专业毕设及实战操练,可作课程设计与期末大作业,含全部源码,能直用于毕设,经严格调试,运行有保障!【项目服务】:有任何使用上的问题,欢迎随时与博主沟通,博主会及时解答。 经导师精心指导并认可、获 98 分的毕业设计项目!【项目资源】:微信小程序。【项目说明】:聚焦计算机相关专业毕设及实战操练,可作课程设计与期末大作业,含全部源码,能直用于毕设,经严格调试,运行有保障!【项目服务】:有任何使用上的问题,欢迎随时与博主沟通,博主会及时解答。 经导师精心指导并认可、获 98 分的毕业设计项目!【项目资源】:微信小程序。【项目说明】:聚焦计算机相关专业毕设及实战操练,可作课程设计与期末大作业,含全部源码,能直用于毕设,经严格调试,运行有保障!【项目服务】:有任何使用上的问题,欢迎随时与博主沟通,博主会及时解答。 经导师精心指导并认可、获 98 分的毕业设计项目!【项目资源】:微信小程序。【项目说明】:聚焦计算机相关专业毕设及实战操练,可作课程设计与期末大作业,含全部源码,能直用于毕设,经严格调试,运行有保障!【项目服务】:有任何使用上的问题,欢迎随时与博主沟通,博主会及时解答。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值