2017-09-12 23 views
2

シングルトンQtの実装を探していて、thisが見つかりました。私はそれについていくつか質問があります。Qtシングルトンの実装

  1. createQBasicAtomicPointerとする目的は何ですか?
  2. qCallOnceのポイントは、以前はfetchAndStoreAcquireを使用していた場合はtestAndSetRelaxedを使用していますか?獲得セマンティクは、後でメモリの並べ替えをすでに妨げていませんか?
  3. qCallOncePerThread機能の目的は何ですか? qCallOnceはすでにスレッドセーフではありませんか?

私はここで提案し、実装の内容をコピー:

// 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 
を使用する方法
#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 

singleton.h

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 

シングルトンパターンを用いる

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; 
} 
+0

「提案」と「シングルトン」という2つの単語は、通常、質問でうまくいっていません。私はシングルトンを使用しないことをお勧めします:P – user463035818

+0

本当にシングルトンの怠惰な読み込みが必要ですか?このような回避策は、C++ 03以降との互換性のためにのみ提供されているためです。 C++ 11を使用している場合、シングルトンは2行のコードで作成されることがあります。 –

+0

本当にありません。あなたがその回避策の理由を私に指摘できたら、感謝します。 – perencia

答えて

0

主な概念は1つに一般的に使用され、オブジェクトの一定数のインスタンス化を制限することです。

Q1:原子ポインタ

アトミック操作はそれほど扱いマルチスレッドインスタンス呼び出し、中断することなく行われます。

Q2:qCallOnce

の点

他のスレッドが実行し、そうであれば

do { 
      QThread::yieldCurrentThread(); 
     } 
    while (!flag.testAndSetAcquire(CO_Finished, CO_Finished)); 

Q3にCO_finishedフラグを待つている場合は、この機能をチェック:qCallOncePerThread

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

I各スレッドごとにシングルトンクラスのLocalDataを処理すると思います

+0

ありがとうございましたが、私はより深い答えを探していました。私を明確にしましょう。 Q1。 'flag'変数は' qCallOnce'をスレッドセーフにするためのアトミックであることを理解していますが、なぜ 'create'変数ですか?私はその特定の変数には何の利益も見ません。 Q2。私は 'testAndSetAcquire'の後に' testAndSetRelaxed'を具体的に求めていました。 – perencia

1

次のシングルトン実装を使用するには十分だと思います。私が覚えているように、静的変数にはインスタンス化/初期化は1つしかありません。 オリジナルの問題は、複数のスレッドが同じ時間にインスタンスにアクセスしようとしたときに、シングルトンが2回作成されたときに状況が発生した場合です。

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(); 
+0

'D'は、ラップされたクラスを多態的に使用できるようにすることですか? – perencia

+0

@perenciaはい。私のutilsのテンプレートです。 –

関連する問題