www.cnblogs.com/myd620/p/61…

In some applications, only one instance of a class is allowed, which is known as the singleton pattern. Singleton mode is divided into slacker mode and hungry mode.

Firstly, the realization of hungry and hungry mode is given

template <class T> class singleton { protected: singleton(){}; private: singleton(const singleton&){}; Singleton & operator=(const singleton&){}; Static T* m_instance; public: static T* GetInstance(); }; template <class T> T* singleton<T>::GetInstance() { return m_instance; } template <class T> T* singleton<T>::m_instance = new T();Copy the code

When the m_Instance variable is instantiated, the constructor of the class is called directly. As the name implies, m_instance is assigned before the variable is used, like a hungry feeling. This mode is certainly thread-safe in a multi-threaded environment because there is no problem with multi-threaded instantiation.

Let’s look at slacker mode

template <class T>
class singleton
{
protected:
    singleton(){};
private:
    singleton(const singleton&){};
    singleton& operator=(const singleton&){};
    static T* m_instance;
public:
    static T* GetInstance();
};


template <class T>
T* singleton<T>::GetInstance()
{
    if( m_instance == NULL)
    { 
        m_instance = new T();
    }
    return m_instance;
}

template <class T>
T* singleton<T>::m_instance = NULL;
Copy the code

In lazy mode, the m_instance variable is equal to NULL, and the GetInstance() method is called to determine whether to assign. This pattern is not thread-safe, as multiple threads calling the GetInstance() method at the same time can result in multiple instances. To achieve thread-safety, locks are necessary.

The improved code is shown below

template <class T>
class singleton
{
protected:
    singleton(){};
private:
    singleton(const singleton&){};
    singleton& operator=(const singleton&){};
    static T* m_instance;
    static pthread_mutex_t mutex;
public:
    static T* GetInstance();
};


template <class T>
T* singleton<T>::GetInstance()
{
    pthread_mutex_lock(&mutex);
    if( m_instance == NULL)
    { 
        m_instance = new T();
    }
    pthread_mutex_unlock(&mutex);
    return m_instance;
}


template <class T>
pthread_mutex_t singleton<T>::mutex = PTHREAD_MUTEX_INITIALIZER;

template <class T>
T* singleton<T>::m_instance = NULL;
Copy the code

It all seems perfect, but the programmed ape is an animal with a natural lack of satisfaction. They found that the GetInstance() method, which has to be locked every time it comes in, was inefficient. However, this is not necessary, so the GetInstance() method is improved

 

template <class T>
T* singleton<T>::GetInstance()
{
    if( m_instance == NULL)
    {
        pthread_mutex_lock(&mutex);
        if( m_instance == NULL)
        { 
             m_instance = new T();
        }
        pthread_mutex_unlock(&mutex);
    }
    return m_instance;
}
Copy the code

 

This is known as the “double lock” mechanism. When m_instance = new T() is executed, m_instance may have a value before T is initialized. This will cause another thread calling GetInstance() to get the m_Instance pointer that has not yet been initialized, which could have unintended consequences if used. In fact, the solution is also very simple, use a local variable transition can be:

template <class T>
T* singleton<T>::GetInstance()
{
    if( m_instance == NULL)
    {
        pthread_mutex_lock(&mutex);
        if( m_instance == NULL)
        { 
             T* ptmp = new T();
             m_instance = ptmp;
        }
        pthread_mutex_unlock(&mutex);
    }
    return m_instance;
}
Copy the code

At this point in slacker mode, it’s safe to thread.

However, there is another implementation under Linux. Linux provides a function called pthread_once(), which ensures that a function is executed only once in a process. Here is the thread-safe lazy singleton pattern implemented using pthread_once

template <class T>
class singleton
{
protected:
    singleton(){};
private:
    singleton(const singleton&){};
    singleton& operator=(const singleton&){};
    static T* m_instance;
    static pthread_once_t m_once;
public:
    static void Init();
    static T* GetInstance();
};


template <class T>
void singleton<T>::Init()
{
    m_instance = new T();
}

template <class T>
T* singleton<T>::GetInstance()
{
    pthread_once(&m_once,Init);
    return m_instance;
}

template <class T>
pthread_once_t singleton<T>::m_once = PTHREAD_ONCE_INIT;

template <class T>
T* singleton<T>::m_instance = NULL;
Copy the code

 

The above singleton class uses templates to instantiate a unique instance of each type of variable.

For example, to instantiate an int

int *p = singleton<int>::GetInstance()

For example, you want to instantiate a string

string *p = singleton<string>::GetInstance()

In the above implementation, the GetInstance() function is called with no arguments when the object is instantiated. This is because different objects are initialized with a different number of arguments. If you want to support object initialization with parameters of different types, you need to override the GetInstance function. In c++11, however, variadic functions are already supported. Here’s a simple example

#ifndef _SINGLETON_H_
#define _SINGLETON_H_

template <class T>
class singleton
{
protected:
    singleton(){};
private:
    singleton(const singleton&){};
    singleton& operator=(const singleton&){};
    static T* m_instance;
public:
    template <typename... Args>
    static T* GetInstance(Args&&... args)
    {
        if(m_instance == NULL)
            m_instance = new T(std::forward<Args>(args)...);
        return m_instance;
    }


    static void DestroyInstance()
    {
        if(m_instance )
            delete m_instance;
        m_instance = NULL;
    }
};


template <class T>
T* singleton<T>::m_instance = NULL;

#endif
Copy the code

Test functions

#include <iostream> #include <string> #include "singleton.h" using namespace std; struct A { A(int a ,int b):_a(a),_b(b) {} int _a; int _b; }; int main() { int *p1 = singleton<int>::GetInstance(5); int *p2 = singleton<int>::GetInstance(10); cout << *p1 << " " << *p2 <<endl; string *p3 = singleton<string>::GetInstance("aa"); string *p4 = singleton<string>::GetInstance("bb"); cout << *p3 << " " << *p4 <<endl; A * (p5 = singleton < A > : : GetInstance (1, 2); A * p6 = singleton < A > : : GetInstance (4, 5); cout << p5->_a << " " << p6->_a<<endl; return 0; }Copy the code

The result is as follows

Tags: design patterns, multithreading, c++

\