C++实现线程安全的单例模式
在某些应⽤环境下⾯,⼀个类只允许有⼀个实例,这就是著名的单例模式。单例模式分为懒汉模式,跟饿汉模式两种。
⾸先给出饿汉模式的实现
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();
在实例化m_instance 变量时,直接调⽤类的构造函数。顾名思义,在还未使⽤变量时,已经对m_instance进⾏赋值,就像很饥饿的感觉。这种模式,在多线程环境下肯定是线程安全的,因为不存在多线程实例化的问题。
下⾯来看懒汉模式
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();
}
java单例模式懒汉和饿汉return m_instance;
}
template <class T>
T* singleton<T>::m_instance = NULL;
懒汉模式下,在定义m_instance变量时先等于NULL,在调⽤GetInstance()⽅法时,在判断是否要赋值。这种模式,并⾮是线程安全的,因为多个线程同时调⽤GetInstance()⽅法,就可能导致有产⽣多个实例。要实现线程安全,就必须加锁。
下⾯给出改进之后的代码
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();
};
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;
这⼀切看起来都很完美,但是程序猿是⼀种天⽣就不知道满⾜的动物。他们发现GetInstance()⽅法,每次进来都要加锁,会影响效率。然⽽这并不是必须的,于是⼜对GetInstance()⽅法进⾏改进
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;
}
这也就是所谓的“双检锁”机制。但是有⼈质疑这种实现还是有问题,在执⾏ m_instance = new T()时,可能类T还没有初始化完
成,m_instance 就已经有值了。这样会导致另外⼀个调⽤GetInstance()⽅法的线程,获取到还未初始化完成的m_instance 指针,如果去使⽤它,会有意料不到的后果。其实,解决⽅法也很简单,⽤⼀个局部变量过渡下即可:
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;
}
到这⾥在懒汉模式下,也就可以保证线程安全了。
然⽽,在linux下⾯还有另⼀种实现。linux提供了⼀个叫pthread_once()的函数,它保证在⼀个进程中,某个函数只被执⾏⼀次。下⾯是使⽤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();
};
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;
上⾯的单例类使⽤了模板,对每⼀种类型的变量都能实例化出唯⼀的⼀个实例。
例如要实例化⼀个int类型
int *p = singleton<int>::GetInstance()
例如要实例化⼀个string类型
string *p = singleton<string>::GetInstance()
在上⾯的实现中,在实例化对象时,调⽤GetInstance()函数时都没有传递参数,这是犹豫不同的对象其初始化时参数个数都不⼀样。如果要⽀持不同类型的对象带参数初始化,则需要重载GetInstance函数。然⽽在c++11中,已经⽀持了可变参数函数。这⾥给出⼀个简单的例⼦
#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 & 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
测试函数
#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; return0;
}
运⾏结果如下

版权声明:本站内容均来自互联网,仅供演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系QQ:729038198,我们将在24小时内删除。