虚拟机中Qt信号无法连接

3
我使用RedHat 7.4,GCC 4.8.5和Qt 4.8.5进行开发和编译。代码必须进行静态链接,然后在运行Scientific Linux release 6.7的虚拟机上执行。为了避免依赖于更新的GLIBC >= 2.4,使用了memcpy-Wrap。
以下是我的最小工作示例:
#include <iostream>
#include <unistd.h>
#include <QtCore>
#include <QThread>

__asm__(".symver memcpy, memcpy@GLIBC_2.2.5");
extern "C" {
void *__wrap_memcpy(void *dest, const void *src, size_t n) { return memcpy(dest, src, n); }
}

class Worker : public QThread {
    void run() {
        std::cout << "WORKER: Started." << std::endl;
        QObject::connect(this, SIGNAL(finished()), QCoreApplication::instance(), SLOT(quit()));
        int i=0;
        while(i++<3) {
            std::cout << "WORKER: I am running." << std::endl;
            usleep(1e6);
        }
        std::cout << "WORKER: Finished." << std::endl;
    }
};

int main(int argc, char** argv) {

    std::cout << "MAIN: Init QCoreApplication" << std::endl;
    QCoreApplication qtApplication(argc, argv);

    std::cout << "MAIN: Init Worker" << std::endl;
    Worker myWorker;
    myWorker.start();

    std::cout << "MAIN: Start Event-Loop." << std::endl;
    qtApplication.exec();

    std::cout << "MAIN: Event-Loop finished." << std::endl;
    return 0;
}

这段代码是在RedHat系统上编译的

g++ -I$QTD/mkspecs/linux-g++ -I$QTD/include -I$QTD/include/QtCore -o mwe mwe.cpp  -Wl,--wrap=memcpy -L$QTD/lib/ -lQtCore -lQtNetwork -lglib-2.0 -lrt -lpthread -ldl -lz

这里的$QTD是我的Qt-4.8.5安装路径。

在Red-Hat系统上,我们期望并观察到以下行为:

MAIN: Init QCoreApplication
MAIN: Init Worker
MAIN: Start Event-Loop.
WORKER: Started.
WORKER: I am running.
WORKER: I am running.
WORKER: I am running.
WORKER: Finished.
MAIN: Event-Loop finished.

以下行为在Scientific-Linux系统上被观察到:
MAIN: Init QCoreApplication
MAIN: Init Worker
MAIN: Start Event-Loop.
WORKER: Started.
WORKER: I am running.
WORKER: I am running.
WORKER: I am running.
WORKER: Finished.

然后这个应用程序永远无法完成。

在Red-Hat系统中,似乎工作线程的完成信号与核心应用程序的退出槽相连。但在Scientific-Linux系统中似乎没有发生这种情况。请问有什么建议可以解决这个问题并如何进行调试?


@G.M. 我建议优先选择Qt5信号和lambda表达式,但OP表示使用的是Qt 4.8.5版本。这个版本是否已经支持Qt5信号? - Scheff's Cat
我认为你们两个都在谈论新的签名,其中我不必将大写字母"SIGNAL"和"SLOT"放在那里,而只需提供类的方法名称?是的,我也理解这仅适用于Qt5版本,但并不适用于我的版本。 - Tobias
你可以尝试建立 Qt::DirectConnection 类型的信号/槽连接。我猜测你的信号已经被发射了,但是槽没有被调用,因为 qtApplication.exec(); 阻塞了主事件循环。 - vahancho
1
你尝试过在你的工作线程结束时手动调用退出槽函数而不使用连接吗?使用QMetaObject::invokeMethod,它可以在线程之间同样地工作。 - xander
@xander:是的,我刚刚尝试了那个,结果一样。 - Tobias
显示剩余5条评论
2个回答

0

正如所提到的 - 在Qt中使用线程的方式并不推荐。但对于您的情况来说是可以的。无论如何,我建议您尝试下一种方法:

class Worker : public QThread {
    Q_OBJECT // Don't forget this macro
    void run() {
        std::cout << "WORKER: Started." << std::endl;
        // QObject::connect(this, SIGNAL(finished()), QCoreApplication::instance(), SLOT(quit()));
        int i=0;
        while(i++<3) {
            std::cout << "WORKER: I am running." << std::endl;
            usleep(1e6);
        }
        std::cout << "WORKER: Finished." << std::endl;
        this->deleteLater();
    }
};

int main(int argc, char** argv) {

    std::cout << "MAIN: Init QCoreApplication" << std::endl;
    QCoreApplication qtApplication(argc, argv);

    std::cout << "MAIN: Init Worker" << std::endl;
    Worker *myWorker = new Worker();
    QObject::connect( myWorker, SIGNAL(destroyed()), &qtApplication, SLOT(quit());
    myWorker->start();

    std::cout << "MAIN: Start Event-Loop." << std::endl;
    qtApplication.exec();

    std::cout << "MAIN: Event-Loop finished." << std::endl;
    return 0;
}

1
我以为只有在派生类中声明信号/槽时才需要Q_OBJECT?Worker类仅重载run()方法,因此不需要宏,对吗? - Tobias
它在SL系统上表现出与以前相同的行为。 - Tobias
不知道... 顺便试试这个,在主函数中:QTimer::singleShot(100, &app, SLOT(quit())); - 行得通吗? - Dmitry Sazonov
我在主线程中使用了QTimer::singleShot(1e4, &qtApplication, SLOT(quit()));。然后工作线程在大约3秒钟内完成,应用程序在10秒钟后自动退出。因此,qtApplication的事件循环确实是活动的,并且可以通过其自己的线程信号进行访问和监听。 - Tobias
@Tobias,你能否请在Worker类中添加一个插槽并将其连接到自己的finished信号上,以测试信号是否被发射(在插槽内部放置qDebug << "...")。 - p-a-o-l-o
请尝试使用零第一个参数从工作线程执行此操作。QTimer::singleShot(0, qApp, SLOT(quit())); - Dmitry Sazonov

0

你必须正确使用QThread。

重载run()函数是不安全的,而Nokia以前接受过,并展示了如何使用QThread。

查看此文档


谢谢您的建议,但我想我的同事之前尝试过这种方法,并告诉我它也没有起作用。我现在会再试一次。 - Tobias
2
请注意,这个问题存在争议:https://woboq.com/blog/qthread-you-were-not-doing-so-wrong.html(请注意,这是一条评论,而不是答案)。 - p-a-o-l-o

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