Home arrow C++ arrow Page 5 - The Singleton Pattern Revisited
C++

The Singleton Pattern Revisited


A reader wrote in some time after we ran a short series on the Singleton pattern with some questions. These included how to construct a singleton when the class has a non-trivial constructor with arguments that are calculated at run-time, and how to use the singleton in a multi-threaded environment. If you were wondering about these issues as well, keep reading.

Author Info:
By: J. Nakamura
Rating: 4 stars4 stars4 stars4 stars4 stars / 19
May 02, 2005
TABLE OF CONTENTS:
  1. · The Singleton Pattern Revisited
  2. · The creation of a Singleton
  3. · Policy-based Class Design
  4. · Tweaking the Singleton Class with Policies
  5. · Singleton's Thread Safety

print this article
SEARCH DEVARTICLES

The Singleton Pattern Revisited - Singleton's Thread Safety
(Page 5 of 5 )

The singleton presented here is not thread safe at all. It will work fine for a single threaded application, but there is a location in the code that will have to be synchronized in order for this code to work safely in a multithreaded application!

Take a closer look at the Instance() function:

static T& Instance() {

if (!m_pInstance)

m_pInstance = CreationPolicy<T>::Create();

return *m_pInstance;

}

What would happen when one thread enters the function and leaves it just after it executed ‘if (!m_pInstance)’? Another threads switches in, sees that m_pInstance is not instantiated yet… runs through the code and creates m_pInstance in the process. As soon as the first thread continues execution, it will create m_pInstance for the second time!

That is why programmers were given mutexes… to synchronize the access to sections of code. So would the following change make the problem go away?

static T& Instance() {

Guard lock(m_mutex);

if (!m_pInstance)

m_pInstance = CreationPolicy<T>::Create();

return *m_pInstance;

}

Guard is an object here that acquires/locks the mutex (declared as a member variable of the Singleton class) in its constructor and releases/frees it in the destructor. It is very useful to use a class like this, since you won’t be able to forget to release the mutex when the code execution leaves the scope where it was acquired, and at the same time it solves our problem!

Those of you familiar with multithreaded programming and who are in need of performance as well: getting a lock on a mutex is a very expensive operation! And since the Instance() function is the main entry point to our singleton class (used every time we need to access the object contained in that singleton), this solution causes our application to take a serious performance hit. So we do not want to lock and unlock a mutex every time the Instance() function is used; we only want to do that when we are initializing the singleton and constructing the object contained inside the singleton.

Another try could be:

static T& Instance() {

if (!m_pInstance) {

Guard lock(m_mutex);

m_pInstance = CreationPolicy<T>::Create();

}

return *m_pInstance;

}

This only locks the mutex when m_pInstance has to be initialized, but it reintroduces the race condition. Again it is possible for one thread to be switched out of context after the ‘if statement’ and to have another thread coming in to create m_pInstance, only to have the first thread create it a second time when it resumes its execution.

What we need is the ‘ Double-Checked Locking Pattern’ in this context, a pattern devised by Douglas Schmidt and Tim Harrison. If you write a lot of multithreaded or network code across different platforms, I advise you to take a look at the Adaptive Communication Library [ACE]. It is available for free from professor Douglas C. Schmidt and I use it quite often. It enabled me, for example, to reuse the same source tree that utilized threads and sockets on IBM AIX, Sun Solaris, Linux, Win32, and HP-UX.

Here is the solution with the Double-Checked Locking Pattern:

static T& Instance() {

if (!m_pInstance)

{

Guard lock(m_pInstance);

if (!m_pInstance)

m_pInstance = CreationPolicy<T>::Create();

}

return *m_pInstance;

}

There is only one thread allowed in the section after the locked mutex. If thread one was to switch out of context right after the ‘if statement’ and another thread would grab hold of the mutex and create m_pInstance, the first thread wouldn’t try to create it for a second time because of the second ‘if statement’ (remember that it will have to wait until the mutex is released as well). It might look a bit peculiar, but you will need this if you are going to use the Singleton class in a multithreaded environment.

With some luck I have provided some answers for those who didn’t mail me to ask the questions. Still, if you do have any questions, please don’t hesitate to mail them to me, because you most probably will end up helping others (and me) in the end as well!

References.

[Gamma] Design Patterns: E.Gamma, R.Helm, R.Johnson and J.Vlissides.

[Alexandrescu] Modern C++ Design: A.Alexandrescu.

[Meyers] More Effective C++: S.Meyers.

[ACE] http://www.cs.wustl.edu/~schmidt/ACE.html

Thank you to J. Witte for his questions.


DISCLAIMER: The content provided in this article is not warranted or guaranteed by Developer Shed, Inc. The content provided is intended for entertainment and/or educational purposes in order to introduce to the reader key ideas, concepts, and/or product reviews. As such it is incumbent upon the reader to employ real-world tactics for security and implementation of best practices. We are not liable for any negative consequences that may result from implementing any information covered in our articles or tutorials. If this is a hardware review, it is not recommended to open and/or modify your hardware.

blog comments powered by Disqus
C++ ARTICLES

- Intel Threading Building Blocks
- Threading Building Blocks with C++
- Video Memory Programming in Text Mode
- More Tricks to Gain Speed in Programming Con...
- Easy and Efficient Programming for Contests
- Preparing For Programming Contests
- Programming Contests: Why Bother?
- Polymorphism in C++
- Overview of Virtual Functions
- Inheritance in C++
- Extending the Basic Streams in C++
- Using Stringstreams in C++
- Custom Stream Manipulation in C++
- General Stream Manipulation in C++
- Serialize Your Class into Streams in C++

Watch our Tech Videos 
Dev Articles Forums 
 RSS  Articles
 RSS  Forums
 RSS  All Feeds
Write For Us 
Weekly Newsletter
 
Developer Updates  
Free Website Content 
Contact Us 
Site Map 
Privacy Policy 
Support 

Developer Shed Affiliates

 




© 2003-2017 by Developer Shed. All rights reserved. DS Cluster - Follow our Sitemap
Popular Web Development Topics
All Web Development Tutorials