使用Qt编写线程安全的单例模式

版权声明: 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

使用的话参照前面的链接。暂时只是弄懂了大概是如何实现的,但感觉还是有很多精髓没有吃透,记录一下之后随着学习的深入再慢慢理解吧。

猜你喜欢

转载自blog.csdn.net/guimaxingtian/article/details/82798257