Singleton Design Pattern and Thread Safety
来源:互联网 发布:淘宝升级企业店铺坏处 编辑:程序博客网 时间:2024/06/05 12:57
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?
- This article is for people who know about Singletons and want to learn about thread safety for Singletons.
- 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:
- What thread safety means.
- Basic understanding of Singleton – you might still be okay if you know about static methods.
- 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 constructorpublic: // 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):
- No client code has called
GetInstance()
so far, and now two threads simultaneously callGetInstance()
, and - Context switch between the two calling threads happen on the exact line of code at:Collapse |Copy Code
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 instantiatedMySingleton
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 accessesGetInstance()
.
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 allGetInstance()
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 inmain()
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 callGetInstance()
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!)
- What if no client calls
MyInstance()
during program execution? Maybe the client ran a use-case this time that did not needMySingleton
's usage. You've created an unnecessary object that's floating around during the entire program life-cycle doing nothing. - While Early or Lazy Instantiation might not sound like a big deal, what if
MySingleton
is a memory-hogging class? What ifMySingleton
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! - 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 callsGetInstance()
.
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 friendMySingleton
. 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 classstatic
.
class MonoState{public: // MonoState functionalities static void foo(); static bool goo(); static int zoo(); MonoState() {} // constructor NEITHER private NOR staticprivate: static int MyData;};
Clients of MonoState
access it by creating an object and calling the static method. Since all methods and variables arestatic
, 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 underThe Code Project Open License (CPOL)
India
- Singleton Design Pattern and Thread Safety
- Spring Singleton, Request, Session Beans and Thread Safety
- Reentrancy and Thread-Safety
- Thread-safety and POSIX.1
- Thread Safety and Shared Resources
- Design Pattern 8-singleton
- [Design Pattern]:Singleton
- design pattern : Singleton
- Design pattern--Singleton
- C++ Singleton design pattern
- java singleton design pattern
- Singleton Design Pattern
- Singleton Design Pattern
- SingleTon Design Pattern
- Design pattern: Singleton
- Design Pattern: Singleton 模式
- java design pattern - singleton
- Design Pattern:Singleton
- Android Intent的几种用法全面总结
- 原来浏览器都有查看网页源代码的功能!
- 四中启动模式以及onNewIntent(Intent intent)
- 选择计算机语言
- 将阿拉伯数字转化成大写的人民币数字
- Singleton Design Pattern and Thread Safety
- C++ 虚函数,纯虚函数
- word 表头分页重复
- PHP实现简单的socket通信
- 转载 : 被占用文件操作三法 [Ms-Rem]
- 防止表单提交按钮多次提交的办法
- Linux 开启vnc服务
- Java 注解(annotation)
- 从EXCEL抓数据到mysql