版权声明: https://blog.csdn.net/guimaxingtian/article/details/82798257
使用Qt编写线程安全的单例模式
代码参考http://wiki.qt.io/Qt_thread-safe_singleton
刚接触这段代码时,觉得很多地方不懂,主要是自己对C++的多线程知识了解的还不够深入,研究了两天之后,记录一下,纵然还是不能完全掌握其中精髓,但还是学到了一些东西。
//call_once.h
#ifndef CALL_ONCE_H
#define CALL_ONCE_H
#include <QtGlobal>
#include <QAtomicInt>
#include <QMutex>
#include <QWaitCondition>
#include <QThreadStorage>
#include <QThread>
namespace CallOnce {
enum ECallOnce {
CO_Request,
CO_InProgress,
CO_Finished
};
//Q_GLOBAL_STATIC宏主要用于设置non-POD类型的全局静态变量
//QThreadStorage用于按线程存储数据,所以对应的不同的线程会存储不同的once_flag值
Q_GLOBAL_STATIC(QThreadStorage<QAtomicInt*>, once_flag)
}
template <class Function>
inline static void qCallOnce(Function func, QBasicAtomicInt& flag)
//flag是引用,当第一次调用结束后,flag对应的值就会被设为co_finished
{
using namespace CallOnce;
#if QT_VERSION < 0x050000
int protectFlag = flag.fetchAndStoreAcquire(flag);
#elif QT_VERSION >= 0x050000
int protectFlag = flag.fetchAndStoreAcquire(flag.load());//将protectFlag初始化为flag
//当第一次调用结束后,该值就会被设为co_finished
#endif
if (protectFlag == CO_Finished)
{
//qDebug()<<"fi--"<<"flag: "<< flag<<"protectFlag: "<<protectFlag;
return;
}
//如果flag的当前值等于protectFlag(CO_Request),则将CO_InProgress赋给flag并返回true
if (protectFlag == CO_Request && flag.testAndSetRelaxed(protectFlag,
CO_InProgress)) {
func();
//qDebug()<<"re1--"<<"flag: "<< flag<<"protectFlag: "<<protectFlag;
flag.fetchAndStoreRelease(CO_Finished);//将CO_Finished赋给flag
//qDebug()<<"re2--"<<"flag: "<< flag<<"protectFlag: "<<protectFlag;
}
else {
do {
//将当前线程的执行权让给别的可执行线程,至于让给哪一个可执行线程,由操作系统决定
//qDebug()<<"ot--"<<"flag: "<< flag<<"protectFlag: "<<protectFlag;
QThread::yieldCurrentThread();
}
//如果flag不等于CO_Finished,则继续执行循环体
while (!flag.testAndSetAcquire(CO_Finished, CO_Finished));
}
}
template <class Function>
inline static void qCallOncePerThread(Function func)
{
using namespace CallOnce;
if (!once_flag()->hasLocalData()) {//如果once_flag没有在本线程被设置过的话则执行下面语句
once_flag()->setLocalData(new QAtomicInt(CO_Request));
qCallOnce(func, *once_flag()->localData());
}
}
#endif // CALL_ONCE_H
上面的代码主要是用于让函数只执行一次,同时保证了线程安全,不得不说,原子操作相当的骚气。
//signleton.h
#ifndef SINGLETON_H
#define SINGLETON_H
#include <QtGlobal>
#include <QScopedPointer>
#include "call_once.h"
template <class T>
class Singleton
{
private:
typedef T* (*CreateInstanceFunction)();
public:
static T* instance(CreateInstanceFunction create);
private:
static void init();
Singleton();
~Singleton();
Q_DISABLE_COPY(Singleton)
static QBasicAtomicPointer<void> create;
static QBasicAtomicInt flag;
static QBasicAtomicPointer<void> tptr;
bool inited;
};
template <class T>
T* Singleton<T>::instance(CreateInstanceFunction create)
{
Singleton::create.store(create);//Singleton::create存储了传入的create
qCallOnce(init, flag);//这里的init是void Singleton<T>::init(), fla是 static QBasicAtomicInt flag
//在qcallonce中调用init()之后,tptr存储了createFunction的返回值
//qCallOnce保证了init()只被调用一次
return (T*)tptr.load();
}
template <class T>
void Singleton<T>::init()
{
static Singleton singleton;
if (singleton.inited) {
CreateInstanceFunction createFunction = (CreateInstanceFunction)Singleton::create.load();
tptr.store(createFunction());
}
}
template <class T>
Singleton<T>::Singleton() {
inited = true;
}
template <class T>
Singleton<T>::~Singleton() {
T* createdTptr = (T*)tptr.fetchAndStoreOrdered(nullptr);
if (createdTptr) {
delete createdTptr;
}
create.store(nullptr);
}
template<class T> QBasicAtomicPointer<void> Singleton<T>::create = Q_BASIC_ATOMIC_INITIALIZER(nullptr);
template<class T> QBasicAtomicInt Singleton<T>::flag = Q_BASIC_ATOMIC_INITIALIZER(CallOnce::CO_Request);
template<class T> QBasicAtomicPointer<void> Singleton<T>::tptr = Q_BASIC_ATOMIC_INITIALIZER(nullptr);
#endif // SINGLETON_H
使用的话参照前面的链接。暂时只是弄懂了大概是如何实现的,但感觉还是有很多精髓没有吃透,记录一下之后随着学习的深入再慢慢理解吧。