C++Singleton类创建多个实例

C++ Singleton class creates more than one instance

本文关键字:实例 创建 C++Singleton      更新时间:2023-10-16

静态方法GetUI是用鼠标事件调用的,但从调试中注意到,在极少数情况下,构造函数会用鼠标事件的冲击调用两次。

问题是调度程序在构建过程中停止,切换到另一个任务进程调用,该调用也开始创建另一个实例?

    Object* Interface::instance = 0;
    Object* Interface::GetUI() {
        if (instance == 0) {
            instance = new Interface();
        }
        return instance;
    }

您实际上应该锁定singleton,否则,在多线程时,您将创建多个实例
对于C++11,您可以按如下方式使用它。

#include <mutex>
class Singleton 
{
    static Singleton *singletonInstance;
    Singleton() {}
    static std::mutex m_;
    public:
    static Singleton* getSingletonInstance()
    {
        std::lock_guard<std::mutex> lock(m_);
        if(singletonInstance == nullptr)
        {
            singletonInstance = new Singleton();
        }
        return singletonInstance;
    }
}

您所描述的行为只能在从多个线程使用GetUI时出现。我希望您知道,如果没有适当的锁定或使用排队的方法调用,就无法对GUI方法进行任何直接调用。

下面显示了在Qt中创建全局变量的线程安全、惯用的方法。真的没有理由再实施一次。有时NIH很糟糕。

#include <QtGlobal>
class Object {};
class Interface {
public:
   static Object * GetUI();
};
// This must be in a *single* source file *only*. Not in header!
Q_GLOBAL_STATIC(Object, interfaceInstance)
Object* Interface::GetUI() {
    return interfaceInstance;
}

问题在于对象的创建和实例化。有两个可能的解决方案;可以同步CCD_ 2功能,正如Jerry YY所建议的,或者您可以确保singleton在开始线程处理之前创建;只出现竞争条件当您在至少一个线程中修改对象时您已经创建了对象,instance永远不会被修改。

一种方法是简单地定义Interface::instance作为:

Object* Interface::instance = Interface::GetUI();

零初始化确保Interface::GetUI调用,Interface::instance初始化为null指针,并且进行静态对象的初始化在CCD_ 7之前。

否则:如果你确信Interface::GetUI永远不会在进入main之前调用(考虑到你说过;在你之前不应该有任何鼠标事件输入main(,然后可以放弃测试(或将其替换为assert( instance != nullptr(在Interface::GetUI中写入:

Object* Interface::instance = new Interface();

简单多了,避免了所有的问题。

这里有一个更好的带有模板的单例实现:

template <class T>
class Singleton 
{
private:
  static T * _instance;
protected:
  Singleton (){}
public:
  static T & get()
  {
    if (_instance)
    {
      return *_instance;
    }
    _instance = new T();
    return *_instance;
  }
};
template <class T>  T* Singleton<T>::_instance = 0;

现在用继承实现它:

class Foo : public Singleton<Foo>

并在任何地方使用:

Foo::get().DoSomething();

这是早期解决方案的更快版本(减少了互斥体开销的使用(。

#include <mutex>
class Singleton 
{
    static volatile Singleton * singletonInstance;
    Singleton() {}
    static std::mutex m_;
    public:
    static Singleton* getSingletonInstance()
    {
        if(singletonInstance == nullptr)
        {
            std::lock_guard<std::mutex> lock(m_);
            if(singletonInstance == nullptr) {
                Singleton *tmp = new Singleton(); // fight with compiler see link in comment
                singletonInstance = tmp;
            }
        }
        return singletonInstance;
    }
}

无论如何,我不相信多线程是一个问题,因为您已经写过鼠标事件,这应该只来自主线程。添加断言Q_ASSERT(QThread::currentThread() == QCoreApplication::instance()->thread())以确保这是同步的问题(如果它失败,请检查调用堆栈(。

我还建议禁用赋值运算符和复制构造函数。

您的实例和GetUI((方法必须是静态的。