Qt Singleton实施

时间:2017-09-12 09:21:12

标签: c++ multithreading qt thread-safety

我正在寻找Singleton Qt实现并找到了this。但我对此有一些疑问。

  1. create成为QBasicAtomicPointer的目的是什么?
  2. 如果之前我们使用过fetchAndStoreAcquire,qCallOnce使用testAndSetRelaxed有什么意义?获取语​​义是否已经阻止了之后的任何内存重新排序?
  3. qCallOncePerThread功能的目的是什么? Isn' t qCallOnce已经是线程安全的吗?
  4. 我在这里复制建议实施的内容:

    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(QThreadStorage<QAtomicInt*>, once_flag)
    }
    
    template <class Function>
    inline static void qCallOnce(Function func, QBasicAtomicInt& flag)
    {
        using namespace CallOnce;
    
    #if QT_VERSION < 0x050000
        int protectFlag = flag.fetchAndStoreAcquire(flag);
    #elif QT_VERSION >= 0x050000
        int protectFlag = flag.fetchAndStoreAcquire(flag.load());
    #endif
    
        if (protectFlag == CO_Finished)
            return;
        if (protectFlag == CO_Request && flag.testAndSetRelaxed(protectFlag,
                                                               CO_InProgress)) {
            func();
            flag.fetchAndStoreRelease(CO_Finished);
        }
        else {
            do {
                QThread::yieldCurrentThread();
            }
            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()->setLocalData(new QAtomicInt(CO_Request));
            qCallOnce(func, *once_flag()->localData());
        }
    }
    
    #endif // CALL_ONCE_H
    

    singleton.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);
      qCallOnce(init, flag);
      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
    

    如何使用

    // myclass.h
    
    #ifndef MYCLASS_H
    #define MYCLASS_H
    
    #include <QObject>
    
    class MyClass : public QObject
    {
        Q_OBJECT
    
    private:
        MyClass(QObject* parent = 0);
        static MyClass* createInstance();
    public:
        ~MyClass();
        static MyClass* instance();
    };
    
    #endif // MYCLASS_H
    
    
    // myclass.cpp
    
    #ifndef MYCLASS_H
    #define MYCLASS_H
    
    #include <QObject>
    #include "singleton.h"
    
    MyClass::MyClass(QObject* parent):
     QObject(parent)
    {
    }
    
    MyClass* MyClass::createInstance()
    {
        return new MyClass();
    }
    
    MyClass::~MyClass()
    {
    }
    
    MyClass* MyClass::instance()
    {
        return Singleton<MyClass>::instance(MyClass::createInstance);
    }
    
    #endif // MYCLASS_H
    

    的main.cpp

    #include <QTextStream>
    #include "myclass.h"
    
    #define MyClassInstance Singleton<MyClass>::instance()
    
    int main(int argc, char* argv[])
    {
        QTextStream(stdout) << MyClass::instance()->metaObject()->className() << endl;
        return 0;
    }
    

2 个答案:

答案 0 :(得分:2)

我认为这将足以使用下一个单例实现。我记得,C ++ 11保证静态变量只有一个实例化/初始化。 原始问题是这样的,当多个线程试图同时访问一个实例并且可能出现这种情况时,单例创建两次。

template <typename T, typename D = T>
class Singleton
{
    friend D;
    static_assert(std::is_base_of_v<T, D>, "T should be a base type for D");

public:
    static T& instance();

private:
    Singleton() = default;
    ~Singleton() = default;
    Singleton( const Singleton& ) = delete;
    Singleton& operator=( const Singleton& ) = delete;
};

template <typename T, typename D>
T& Singleton<T, D>::instance()
{
    static D inst;
    return inst;
}

// Usage:
class MyClass : public Singleton<MyClass>
{
public:
    void foo(){}
};

// Access: 
MyClass::instance().foo();

答案 1 :(得分:0)

使用 Singleton 模式的主要概念是将实例化限制为一定数量的对象,通常用于一个。

Q1:原子指针

原子操作不间断地完成,因此处理了多线程实例调用。

Q2:qCallOnce点

此函数检查是否正在执行任何其他线程,如果是,则等待

中的CO_finished标志
    do {
             QThread::yieldCurrentThread();
       }
    while (!flag.testAndSetAcquire(CO_Finished, CO_Finished));  

Q3:qCallOncePerThread

 if (!once_flag()->hasLocalData()) {
        once_flag()->setLocalData(new QAtomicInt(CO_Request));

我认为每个线程instacnes处理单例类的LocalData