【问题标题】:C++ Singleton gets instantiated twice - problem with static member variableC ++ Singleton被实例化两次 - 静态成员变量的问题
【发布时间】:2018-11-20 16:59:51
【问题描述】:

我已经实现了这样的标准单例模式(非线程版本):

hpp:

#ifndef SERVICEMANAGER_H
#define SERVICEMANAGER_H

#include <QObject>
#include <QVariant>
#include <QMap>
#include "IService.h"
#include "qhcore_global.h"
#include <QDebug>



class  ServiceManager : public QObject
{
    Q_OBJECT

public:
    static ServiceManager*     instance();
    ~ServiceManager(){qDebug()<<Q_FUNC_INFO;}
    IService*           service(QString name);
    bool                registerService(IService* service);

private:
    explicit            ServiceManager(QObject *parent = nullptr);

    QMap<QString, IService*>    _serviceMap;
    static ServiceManager*      __instance;

signals:
    void serviceAdded(QString serviceName);
public slots:
};



#endif // SERVICEMANAGER_H

cpp:

ServiceManager* ServiceManager::__instance = nullptr;

ServiceManager::ServiceManager(QObject *parent) : QObject(parent)
{
     qDebug()<<"CREATE::CTOR";
}

ServiceManager* ServiceManager::instance()
{
    qDebug()<< __instance << "BEFORE";
    if(__instance == nullptr)
    {
        qDebug()<<"CREATE";
        __instance = new ServiceManager();
        qDebug()<< __instance << "AFTER";
    }
    return __instance;
}

我的日志是这样的

[DEBUG    17.11. - 17:50:18:748] --
[DEBUG    17.11. - 17:50:18:748] -- QObject(0x0) BEFORE
[DEBUG    17.11. - 17:50:18:748] -- CREATE
[DEBUG    17.11. - 17:50:18:748] -- CREATE::CTOR
[DEBUG    17.11. - 17:50:18:748] -- ServiceManager(0x55e9fc6fb510) AFTER
[DEBUG    17.11. - 17:50:18:748] -- 0
[INFO     17.11. - 17:50:18:748] -- "Service registered: lab"
[DEBUG    17.11. - 17:50:18:748] -- 1
[INFO     17.11. - 17:50:18:749] -- "Added Resource factory for synclist for descriptor: labcontrol/logs"
[INFO     17.11. - 17:50:18:749] -- "Added Resource factory for synclist for descriptor: labcontrol/users"
[INFO     17.11. - 17:50:18:749] -- "Added Resource factory for synclist for descriptor: labcontrol/permissions"
[INFO     17.11. - 17:50:18:749] -- "Added Resource factory for synclist for descriptor: labcontrol/cards"
[INFO     17.11. - 17:50:43:973] -- "::ffff:192.168.1.32"  has connected.
[INFO     17.11. - 17:50:43:985] -- "admin logged in. (" 1  sessions open)
[INFO     17.11. - 17:50:43:985] -- Device registered:  "4f2o4o7b1a1r"
[INFO     17.11. - 17:50:44:003] -- Create ListResource with FS Resource Handler "logger/mappings_synclist"
[INFO     17.11. - 17:50:44:005] -- Create ListResource with FS Resource Handler "labcontrol/logs_synclist"
[INFO     17.11. - 17:50:44:007] -- Create ListResource with FS Resource Handler "labcontrol/users_synclist"
[INFO     17.11. - 17:50:44:009] -- Create ListResource with FS Resource Handler "labcontrol/permissions_synclist"
[INFO     17.11. - 17:50:44:011] -- Create ListResource with FS Resource Handler "labcontrol/cards_synclist"
[DEBUG    17.11. - 17:50:45:136] -- QObject(0x0) BEFORE
[DEBUG    17.11. - 17:50:45:136] -- CREATE
[DEBUG    17.11. - 17:50:45:136] -- CREATE::CTOR
[DEBUG    17.11. - 17:50:45:137] -- ServiceManager(0x55e9fc749340) AFTER
[DEBUG    17.11. - 17:50:45:137] --  COUNT: 0
[DEBUG    17.11. - 17:50:45:137] -- ()
[WARNING  17.11. - 17:50:45:138] -- "Unavailable servcie: lab"

在日志中可以看到单例会被实例化两次。我第二次调用 instance() 函数时,私有静态成员 __instance 再次持有一个 nullptr。这很奇怪。相同的代码可以在不同平台(OSx、Raspbian、...)的其他机器上完美运行。

我的系统是运行 Ubuntu Server 18.10, x64 的 beelink BT3 pro。代码本身是一个基于 WebSocket 的物联网云,它结合了 Qt5 和插件。

我尝试了什么:

使用不同的编译器,例如 gcc-8、gcc-7 和 gcc-6。 我自己编译 Qt 以确保所有二进制文件都使用相同的编译器构建。

再次:我的应用程序使用插件。这可能与单例对象在不同插件中使用这一事实有关,在运行时通过 QPluginLoader 从共享对象文件加载。

编辑:我不使用线程,也不使用不同的命名空间。

欢迎任何提示!提前致谢!

EDIT2 (解决方案):

(移至下面我的答案。)

【问题讨论】:

  • 这个cpp文件是应用程序的一部分还是插件的一部分?
  • 多线程?初始化时的竞争条件?
  • 与您的问题无关,但请注意,以双下划线开头的符号在任何地方都为编译器和标准库保留。参见例如this old answer 了解更多详情。
  • -没有线程(见编辑)。此代码是插件的一部分。整个应用程序由插件组成。应用程序本身只是一个插件加载器。
  • @FriedemannMetzger 在所以你没有添加 SOLVED 或类似问题的标题,指出问题已解决的方法是将响应标记为正确,如果没有答案帮助您然后我们邀请您发布答案,您可以在 2 天内将其标记为正确。 :-)

标签: c++ qt plugins singleton


【解决方案1】:

您应该使用宏 Q_GLOBAL_STATIC() 在插件之间共享实例。 cpp 文件可以这样改写:

   Q_GLOBAL_STATIC(ServiceManager, myServiceManagerInstance)

    ServiceManager *ServiceManager::instance()
    {
            return myServiceManagerInstance();
    }

【讨论】:

  • 非常感谢!我会尽快试试这个。 - 剩下两个问题:a)为什么我当前的代码在没有修改的情况下可以在所有其他机器上运行,b)构造函数需要公开。所以它不再是一个合适的单例模式。你怎么看?有继承和朋友声明的解决方法。但这看起来有点像一个丑陋的解决方法.. 不是吗?
  • @FriedemannMetzger:您能否修改 instance() 中的第一条日志行以查看更多信息:qDebug()&lt;&lt; __instance &lt;&lt; &amp;__instance &lt;&lt; QThread::currentThreadId() &lt;&lt; "BEFORE";?
  • 不幸的是,这没有奏效。但我找到了解决方案(请参阅我的原始帖子中的 EDIT2) - 非常感谢您的帮助!
  • @FriedemannMetzger 不客气,我看过你的编辑。您是否尝试过 strace 以查看您的应用程序如何加载插件?
【解决方案2】:

如果您将ServiceManager.cpp(或ServiceManager.o)静态链接到多个动态库,每个动态库最终都可能包含自己的ServiceManager::__instance 实例。

解决方案是将ServiceManager.o 放入一个单独的动态库中,供所有“插件”库使用。

【讨论】:

  • 非常感谢您的回答。但我认为这应该已经是这样了。 ServiceManager.cpp 是核心插件的一部分。所有其他插件都与 Core 插件动态链接。
【解决方案3】:

不幸的是,之前的答案没有奏效......仍然是同样的问题。 但是..我发现了问题:

在我的应用程序中有几个独立的插件和核心插件。最后一个是两者:插件和所有其他插件的共享库。 我的应用程序有一个相对搜索路径,它会在其中查找要加载的插件。到目前为止,一切都很好。但是独立的扩展插件需要在运行时找到要链接的核心插件。所以我将核心插件复制到/usr/lib。这就是问题所在。 libCorePlugin.so 有两份副本 - 一份在插件文件夹中,另一份在 /usr/lib 中。我不知道为什么这在所有其他机器上都有效......但是让 LD_LIBRARY_PATH 指向公共插件文件夹而不是将文件复制到 /usr/lib 解决了这个问题。

有人知道为什么这在以前的某些系统上有效吗?

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2016-09-15
    • 1970-01-01
    • 2011-04-11
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2020-12-09
    相关资源
    最近更新 更多