>

Wednesday, 16 December 2015

Singleton

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.
  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.
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;
}
?



0 comments:

Post a Comment