单例模式分为“饥汉”和“饿汉”两种版本,也正是线程安全问题使得原本简单的单例模式变得复杂。由于单例模式很常用,Boost库中有强大的泛型单例实现,我也利用Qt的原子指针QAtomicPointer来实现Qt中的单例模式:
//.cpp
class SingleTon
{
public:
/*! \brief 用于获得SingleTon实例,使用单例模式。
* \return SingleTon实例的引用。
*/
static SingleTon &getInstance(void)
{
//使用双重检测。
if(!instance)//第一次检测
{
QMutexLocker locker(&mutex);//加互斥锁。
if(!instance)//第二次检测。
instance = new SingleTon;
}
return *instance;
}
private:
SingleTon();//禁止构造函数。
SingleTon(const SingleTon &);//禁止拷贝构造函数。
SingleTon & operator=(const SingleTon &);//禁止赋值拷贝函数。
QReadWriteLock internalMutex;//函数使用的读写锁。
static QMutex mutex;//实例互斥锁。
static QAtomicPointer<SingleTon> instance;/*!<使用原子指针,默认初始化为0。*/
};
//静态成员变量初始化。
QMutex SingleTon::mutex;
QAtomicPointer<SingleTon> SingleTon::instance = 0;
双重锁检测在C++中是安全的,另外提供了读写锁,在修改单例数据的函数中使用写锁(QWriteLocker locker(&internalMutex););在读取单例数据的函数中使用读锁(QReadLocker locker(&internalMutex);)。
之前没考虑到乱序执行问题,并且此前代码有严重BUG,即对QAtomicPointer类型变量赋值操作不是原子操作,见官方文档:
For convenience, QAtomicPointer provides pointer comparison, cast, dereference, and assignment operators. Note that these operators are not atomic.
修改代码,使用testAndSetOrdered原子操作,并解决乱序执行问题,testAndSetOrdered特性:
This function uses ordered memory ordering semantics, which ensures that memory access before and after the atomic operation (in program order) may not be re-ordered.
其中关于memory ordering,我转过此帖:http://www.cnblogs.com/codingmylife/archive/2010/04/28/1722573.html
其中关于double checked locking,有此文:http://docs.huihoo.com/ace_tao/lifecycle.html
修改版SingleTon.cpp:
//.cpp 一次修改版
class SingleTon
{
public:
/*! \brief 用于获得SingleTon实例,使用单例模式。
* \return SingleTon实例的引用。
*/
static SingleTon &getInstance(void)
{
#ifndef Q_ATOMIC_POINTER_TEST_AND_SET_IS_ALWAYS_NATIVE
if(!QAtomicPointer::isTestAndSetNative())//运行时检测
qDebug() << "Error: TestAndSetNative not supported!";
#endif
//使用双重检测。
/*! testAndSetOrders操作保证在原子操作前和后的的内存访问
* 不会被重新排序。
*/
if(instance.testAndSetOrdered(0, 0))//第一次检测
{
QMutexLocker locker(&mutex);//加互斥锁。
instance.testAndSetOrdered(0, new SingleTon);//第二次检测。
}
return *instance;
}
private:
SingleTon();//禁止构造函数。
SingleTon(const SingleTon &);//禁止拷贝构造函数。
SingleTon & operator=(const SingleTon &);//禁止赋值拷贝函数。
QReadWriteLock internalMutex;//函数使用的读写锁。
static QMutex mutex;//实例互斥锁。
static QAtomicPointer<SingleTon> instance;/*!<使用原子指针,默认初始化为0。*/
};
另一种实现方式
QT单例模式(函数模板实现)
#ifndef __TSINGLEMODE_H__
#define __TSINGLEMODE_H__
#include <QMutex>
template<typename T>
class TSingleMode
{
protected:
TSingleMode(){}
virtual ~TSingleMode(){}
public:
static T * getInstance()
{
if(m_instance == NULL)
{
m_mutex.lock();
if (m_instance == NULL)
{
m_instance = new T;
}
m_mutex.unlock();
}
return m_instance;
}
//摧毁单例
static void deleteInstance()
{
if(m_instance != NULL)
{
delete m_instance;
m_instance = NULL;
}
}
private:
static T * m_instance;
static QMutex m_mutex; //加入互斥锁,保证线程安全(如果存在多线程)
};
//静态成员变量初始化
template<typename T>
T* TSingleMode<T>::m_instance = NULL;
template<typename T>
QMutex TSingleMode<T>::m_mutex;
#endif
②使用方法
有两种方法,一种是直接调用模板;
另一种是继承模板单例化,例如有一个CSingleMode的类:
CSingleMode.h
#ifndef CSINGLEMODE_H
#define CSINGLEMODE_H
#include "TSingleMode.h"
//第二种方法,继承
class CSingleMode : public TSingleMode<CSingleMode>
{
friend class TSingleMode<CSingleMode>;
public:
CSingleMode();
~CSingleMode();
QString setStr();
private:
int m_load;
};
// 第一种方法,直接调用,上面就不需要继承TSingleMode模板了
// #define g_SingleMode (*TSingleMode<CSingleMode>::getInstance())
#endif // CSINGLEMODE_H
CSingleMode.cpp
#include "CSingleMode.h"
#include <QString>
CSingleMode::CSingleMode()
{
}
CSingleMode::~CSingleMode()
{
}
QString CSingleMode::setStr()
{
return QString("TextLabel_change");
}
那么这个CSingleMode.就是一个单例,可以直接使用,使用方法:
//第一种直接调用模板的方法
g_SingleMode.setStr();
//第二种继承的方法
CSingleMode::getInstance()->setStr();
第二种转自:https://blog.csdn.net/yaojinjian1995/article/details/106214173/