c++ - Qt单例实现

标签 c++ multithreading qt thread-safety

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

  1. 使 create 成为 QBasicAtomicPointer 的目的是什么?
  2. 如果我们之前使用过 fetchAndStoreAcquire,那么在 qCallOnce 中使用 testAndSetRelaxed 有什么意义? acquire 语义不是已经阻止了它之后的任何内存重新排序吗?
  3. qCallOncePerThread 函数的用途是什么? qCallOnce 不是线程安全的吗?

我在这里复制建议实现的内容:

调用一次.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

单例.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

主要.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;
}

最佳答案

我认为这足以使用下一个单例实现。我记得,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();

关于c++ - Qt单例实现,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/46172607/

相关文章:

c++ - 在 Cocos2d 中设置相对于场景的位置

c++ - 当 cin.getline() 的第二个参数大于数组的长度时,字符串长度不可预测

c++ - 为什么在使用 QList 时 std::sort 会崩溃?

qt - 我的信号/插槽连接不起作用

c++ - QTableWidget Item 获取Widget类型并访问它

c++ - 在cpp中动态更改属性值

c++ - Array + int 作为一个参数有什么作用?

java - 使用执行器创建消息监听器池

multithreading - 提供多少逻辑处理器的运行时API是什么?

c# - Asp.net vNext 中的 Thread.Sleep