如何在不阻塞主线程的情况下使用Qt-Dbus绑定

9
我的目标是使用Qt的DBus绑定创建一个库。
我尝试创建一个不需要在主线程中启动QCoreApplication类提供的QEventLoop的Qt应用程序。
这是一个最简应用示例,使用QT-4.6.2版本正常工作,但在使用QT-4.8或更高版本时会阻塞introspection。

DBusHandler.hpp

#pragma once
#include <iostream>
#include <QtCore/QThread>
#include <QtCore/QtCore>
#include <QtDBus/QDBusInterface>

class DBusHandler : public QThread
{
    Q_OBJECT;

private:     
    void run(void)
    {
        QDBusConnection connection = QDBusConnection::sessionBus();

        connection.registerService("my.qdbus.example");
        connection.registerObject("/", this, QDBusConnection::ExportAllSlots);
        exec();
    }

public:
    DBusHandler(void) {}
    virtual ~DBusHandler(void) {}

    void stop(void)
    {
        QDBusConnection connection = QDBusConnection::sessionBus();

        connection.unregisterObject("/");
        connection.unregisterService("my.qdbus.example");
        connection.disconnectFromBus(connection.name());
        QThread::quit();
    }

public slots:
    void remoteCall(QByteArray message)
    {
        std::cout << "Message size: " << message.size() << std::endl;
    }
};

main.cpp

#include "DBusHandler.hpp"

int main(int ac, char **av)
{
    QCoreApplication app(ac, av);
    DBusHandler handler;

    handler.moveToThread(&handler);

    handler.start();
    while (not handler.isRunning());

    // app.exec();
    sleep(10); // Gives time to call using the command line: "qdbus my.qdbus.example / local.DBusHandler.remoteCall a_message"

    handler.stop();
    while (handler.isRunning());
}

如您在main.cpp文件中所见,app.exec()已被注释掉,但在QT-4.8或更高版本(5.3.0)上使应用程序正常工作。
我的问题是:在Qt-4.8或5.3上,是否可以在与主线程不同的其他线程中使用Qt的DBus绑定调用app.exec()

@naab 我很困惑。OP 问:“是否可能在 Qt-4.8 或 5.3 上在主线程上使用 Qt 的 DBus 绑定,不需要 app.exec() 调用?”根据他所展示的内容,难道不应该是 需要 吗? - Tay2510
@Tay2510 这个演示表明,如果不在主线程中执行 app.exec(),代码可以在Qt 4.6.2中工作。期望的解决方案是找到一种不阻塞主线程的方法来使用 Qt EventLoops。在4.8.4版本及以上,是否有可能使用 Qt 作为库而不阻塞主线程,并使用 QEventLoops(使信号/插槽正常工作)? - naab
@naab,所以你的问题和 OP 不同,或者说 OP 的问题问错了?因为你说“不是”可能…而没有阻塞主线程,而 OP 问“是不是”可能…在主线程中没有app.exec()调用。抱歉我不是在玩文字游戏,我只是想确保问题是什么。因为没有 app.exec() 的情况已经被证明可以在 4.8 上工作了,为什么我们还要再次询问是否“没有”? - Tay2510
@naab 不用在意,Jules Baratoux刚刚编辑了这篇帖子。原来的问题似乎有点误导性。 - Tay2510
1个回答

10

背景: 存在一个名为 QDBusConnectionPrivate 的私有类,它继承自 QObject 并处理所有网络连接。不幸的是,如果你查看 qdbusconnection.cpp:1116,你会发现 Qt 将 moveToThread 硬编码为 QCoreApplication::instance()

您可能应该提交增强请求,以允许用户创建使用用户指定线程或事件循环的 QDBusConnection。 请参见下面的更新。

与此同时,如果您习惯于进行一些危险的操作,您可以自己对其进行修改,创建一个自己的 QDbusConnection 子类(我称之为 SpecializedDBusConnection),该子类将 QThread 作为第三个参数来指定要移动到的 QDbusConnectionPrivate 实例所在的线程。然后使用该类来创建连接,而不是默认的 QDbusConnection::sessionBus()

由于这使用了一些私有类,所以需要包含一些私有头文件(在下面的代码中说明),这将尝试包含各种 dbus 库头文件,从而需要修改项目的 INCLUDEPATH 来包含 dbus 库的路径。

我已经验证了此方法适用于 Qt 5.3.0 和 Qt 4.8.6。

更新:Qt 5.6 中,QtDBus 被重构为使用线程 进行传入/传出消息处理;不再阻塞主线程!

DBusHandler.hpp

#pragma once
#include <iostream>
#include <QtCore/QThread>
#include <QtCore/QtCore>
#include <QtDBus/QDBusInterface>
#include <QtDBus/QDBusConnectionInterface>

#include "/path/to/Qt5.3.0/5.3/Src/qtbase/src/dbus/qdbusconnection_p.h"

class SpecializedDBusConnection : public QDBusConnection {
    const char *ownName;
public:
    inline SpecializedDBusConnection(BusType type, const char *name, QThread *thread)
        : QDBusConnection(connectToBus(type, QString::fromLatin1(name))), ownName(name)
    {
        if (QDBusConnectionPrivate::d(*this)) {
            QDBusConnectionPrivate::d(*this)->moveToThread(thread);
        }
    }

    inline ~SpecializedDBusConnection()
    { disconnectFromBus(QString::fromLatin1(ownName)); }
};

class DBusHandler : public QThread
{
    Q_OBJECT;

private:     
    void run(void)
    {
        QDBusConnection connection = SpecializedDBusConnection(QDBusConnection::SessionBus, "qt_default_session_bus", this);

        connection.registerService("my.qdbus.example");
        connection.registerObject("/", this, QDBusConnection::ExportAllSlots);

        exec();
    }
[snip]

1
非常感谢您花时间处理它。我还没有测试过,但我猜这是正确的方法。仍然想知道为什么他们删除了这个 moveToThread 调用。如果您习惯于做一些危险的事情 => 在我看来,您的解决方案远非肮脏/危险的方式来解决这个问题 :)。无论如何,您的发现非常有价值,完全值得奖励! - naab
@Linville 非常感谢您的回复!我会测试您的解决方案。 - ben-du_a

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接