- Back to Home »
- Singleton
Posted by : Sushanth
Wednesday, 16 December 2015
Singleton Pattern:
-----------------------------------------------------------------------
Definition:
“Ensure a class
has one instance, and provide a global point of access to it.”
Restricting a
Class to One Instance
There are times when a class cannot perform correctly if
there is more than one instance of it. The common case is when the class
interacts with an external system that maintains its own global state.Consider a class that wraps an underlying file system API. Because file operations can take a while to complete, our class performs operations asynchronously. This means multiple operations can be running concurrently, so they must be coordinated with each other. If we start one call to create a file, and another one to delete that same file, our wrapper needs to be aware of both to make sure they don’t interfere with each other.
To do this, a call into
our wrapper needs to have access to every previous operation. If users could
freely create instances of our class, one instance has no way of knowing about
operations that other instances started. Enter the Singleton. It provides a way
for a class to ensure at compile time that there is only a single instance of
the class.
Providing a
Global Point of Access
Consider a game where several different systems in the game
will use file system wrapper: logging, content loading, game state saving, etc.
If those systems can’t create their own instances of our file system wrapper,
how do they get ahold of one?
Singleton provides a
solution to this too. In addition to creating the single instance, it also
provides a globally-available method to get it. This way, anyone anywhere can
get their paws on our blessed instance.
Problem:
- Application needs one, and only one, instance of an object.
- Lazy initialization and
- Global access.
Class
Diagram:
Discussion:
- Make the class of the single instance object responsible for
creation, initialization, access, and enforcement.
- Declare the instance as a private static data member
- Provide a public static member function that encapsulates all
initialization code, and provides access to the instance.
- The client calls the accessor function (using the class name
and scope resolution operator) whenever a reference to the single instance
is required.
- Deleting a
Singleton class/instance is a non-trivial design problem.
Example1:
#include <iostream>
using namespace std;
class singleton
{
private:
static singleton *singleton_instance;
int data;
singleton()
{
cout<<"singleton
constructor" <<endl;
}
public:
static singleton *getInstance();
void displaymessage()
{
cout<<"this
is a singleton instance"<<endl;
}
void setdata(int d)
{
data = d;
}
int getdata()
{
return data;
}
};
singleton* singleton::singleton_instance = NULL;
singleton *singleton::getInstance()
{
if(singleton_instance == NULL)
singleton_instance = new
singleton();
return singleton_instance;
}
int main() {
cout << "staring
main() in singleton example" <<endl;
singleton *s = singleton::getInstance();
s->displaymessage();
s->setdata(10);
cout <<"the
data is "<<s->getdata()<<endl;
return 0;
}
In above program,the instance is called when a call to
getInstance() is made and the instance gets deleted when the program exits.
Note2:
If a singleton
instance is deleted in the destructor it results in infinite loop.
To reset the
singleton instance ,add a new static method in the singleton class as shown
below
static void resetInstance()
{
delete singleton_instance;
singleton_instance = NULL;
}
Note
3: How to make singleton class
thread safe.
Thread-safety issue
for Singletons would occur only rarely, as :
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.
Solution 1
Put a mutex lock to the Singleton method to make it thread-safe.
singleton *singleton::getInstance()
{
pthread_mutex_lock(&mutex);
if(singleton_instance == NULL)
singleton_instance = new
singleton();
pthread_mutex_unlock(&mutex);
return singleton_instance;
}
This solution works,
but think about it: locking is a costly operation, and if you are using it each
and every time a client accesses
GetInstance()
.
This is a solution
that works and handles the rare but serious thread safety issue for singletons,
but at the cost of doing an expensive locking operation for all
GetInstance()
calls, slowing
down client access every time!!
Solution 2:
Let's call
MySingleton::GetInstance()
during program start-up, like in main()
in C++.There is only one thread 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.
This works. No
critical section involved, so no costly operation for the general use-case when
clients call
GetInstance()
every time.
Violates the below design principle.
Design
Principle: Late Instantiation means creating
an object when it is required to be used, not up-front.
- 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.
Solution3:
#include <iostream>
using namespace std;
#include <pthread.h>
pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER;
class singleton
{
private:
static singleton *singleton_instance;
int data;
singleton()
{
cout<<"singleton
constructor" <<endl;
}
public:
static singleton *getInstance();
~singleton()
{
singleton_instance = NULL;
}
void displaymessage()
{
cout<<"this
is a singleton instance"<<endl;
}
void setdata(int d)
{
data = d;
}
int getdata()
{
return data;
}
static void resetInstance()
{
delete singleton_instance;
singleton_instance = NULL;
}
};
singleton* singleton::singleton_instance = NULL;
singleton *singleton::getInstance()
{
if(singleton_instance == NULL)
{
pthread_mutex_lock(&mutex);
if(singleton_instance == NULL)
singleton_instance = new
singleton();
pthread_mutex_unlock(&mutex);
}
return singleton_instance;
}
int main() {
cout << "staring
main() in singleton example" <<endl;
singleton *s = singleton::getInstance();
s->displaymessage();
s->setdata(10);
pthread_mutex_destroy(&mutex);
return 0;
}
With Solution 3,
locking is not done every time a client calls
GetInstance()
, and Lazy Instantiation is also achieved. 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()
.
This way of locking is called Double-Checked Locking.
Examples:
1. Reading
configuration files that should only be read at startup time and encapsulating
them in a Singleton.
2. A printer
spooler
Problem:
Subclass Singleton and use same getinstance()
method to create subclass instance
#include <iostream>
using namespace std;
#include <pthread.h>
#include <string>
class shape
{
private:
static shape *s_instance;
int data;
static string shape_type;
protected:
shape()
{
cout<<"the
shape constructor"<<endl;
}
public:
static shape *getInstance();
void setdata(int d) {data = d;}
int getdata(){return data;}
static void settype(string t)
{
shape_type = t;
delete s_instance;
s_instance = NULL;
}
};
shape *shape::s_instance = NULL;
string shape::shape_type = "straight";
class circular:public shape
{
public:
friend class shape;
protected:
circular()
{
cout<<"circular
constructor"<<endl;
}
};
shape* shape::getInstance()
{
if(s_instance == NULL)
{
if(shape::shape_type == "straight")
s_instance = new shape();
else
s_instance = new
circular();
}
return s_instance;
}
int main() {
shape::getInstance()->setdata(10);
shape::settype("circular");
shape::getInstance()->setdata(20);
return 0;
}
?