Qt-Signal не подключается к виртуальной машине

Я разрабатываю и компилирую на RedHat 7.4 с GCC 4.8.5 и Qt 4.8.5. Код должен быть статически связан. Затем выполняется на виртуальной машине под управлением Scientific Linux версии 6.7. memcpy-Wrap используется для предотвращения зависимостей от более новой версии GLIBC >= 2.4.

У меня есть следующий MWE:

#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-System с

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-System:

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-System наблюдается следующее поведение:

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-System готовый сигнал из рабочего потока подключается к выходному слоту в основном приложении. Кажется, этого не происходит в Scientific-Linux-System. Есть ли у кого-нибудь совет, почему это происходит и как я могу это отладить?


person Tobias    schedule 04.04.2018    source источник
comment
Запустите его под отладчиком, чтобы узнать, где он застрял. Кроме того, вместо подключения к QCoreApplication::quit вы можете подключиться к lambda, который вызывает QCoreApplication::quit, что позволит вам добавить дополнительный диагностический вывод.   -  person G.M.    schedule 04.04.2018
comment
@G.M. Я бы предложил предпочесть сигналы Qt5 и лямбда-выражения, но OP заявил, что это Qt 4.8.5. Доступны ли сигналы Qt5 для этой версии?   -  person Scheff's Cat    schedule 04.04.2018
comment
Я думаю, вы оба говорите о новой подписи, в которой мне не нужно писать заглавными буквами СИГНАЛ и СЛОТ, а просто указать имя метода класса? Ага, я тоже так понял, что это только с Qt5 и далее, но недоступно в моей версии.   -  person Tobias    schedule 04.04.2018
comment
@Scheff Извините, я пропустил ограничение Qt4. Это исключает lambda, но функция, которая вызывает QCoreApplication::quit, будет служить той же цели.   -  person G.M.    schedule 04.04.2018
comment
Вы можете попытаться установить соединение сигнал/слот типа Qt::DirectConnection. Я предполагаю, что ваш сигнал испускается, но слот не вызывается, потому что qtApplication.exec(); блокирует основной цикл событий.   -  person vahancho    schedule 04.04.2018
comment
Вы пытались вызвать слот выхода вручную без соединения в конце вашего потока? С QMetaObject::invokeMethod он должен работать одинаково через границы потоков   -  person xander    schedule 04.04.2018
comment
@vahancho: я думал, что мне нужно вызвать qtApplication.exec(), чтобы запустить основной цикл событий? И что этот метод тогда слушает выход из своего слота?   -  person Tobias    schedule 04.04.2018
comment
@xander: Да, я только что попробовал, такое же поведение.   -  person Tobias    schedule 04.04.2018
comment
@Tobias, дело в том, что вы не знаете, когда именно будет вызван qtApplication.exec(). Однако его можно вызвать, пока ваш поток все еще выполняется.   -  person vahancho    schedule 04.04.2018
comment
@vahancho: А, хорошо, но это не имеет значения, я пытался указать DirectConnection или QueuedConnection.   -  person Tobias    schedule 04.04.2018


Ответы (2)


Вы должны правильно использовать QThread. перегрузка функции run() небезопасна, и Nokia раньше приняла это и показала, как использовать QThread.

Посмотрите на это документация.

person CMLDMR    schedule 04.04.2018
comment
Спасибо за предложение, но я думаю, что мой коллега пробовал этот подход раньше и сказал, что он также не работает. Я попробую еще раз сейчас. - person Tobias; 04.04.2018
comment
Обратите внимание, что вокруг этого ведутся споры: woboq.com/ blog/qthread-you-were-not-doing-so-wrong.html (и, пожалуйста, это комментарий, а не ответ). - person p-a-o-l-o; 04.04.2018

Как уже упоминалось - не рекомендуется работать с потоками в 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;
}
person Dmitry Sazonov    schedule 04.04.2018
comment
Я думал, что мне нужен только Q_OBJECT, когда я объявляю сигналы/слоты в своем производном классе? Класс Worker только перегружает метод run(), поэтому он не должен требовать макроса, верно? - person Tobias; 04.04.2018
comment
Он показывает то же поведение, что и раньше в системе SL. - person Tobias; 04.04.2018
comment
Не знаю... Кстати, попробуйте что-то вроде этого, в основной функции: QTimer::singleShot(100, &app, SLOT(quit())); - работает? - person Dmitry Sazonov; 04.04.2018
comment
Я использовал QTimer::singleShot(1e4, &qtApplication, SLOT(quit())); в основном потоке. Затем рабочий процесс завершается примерно через 3 секунды, а приложение завершает работу через 10 секунд. Таким образом, цикл событий qtApplication действительно активен, слушает и может быть достигнут сигналами своего собственного потока. - person Tobias; 04.04.2018
comment
@ Тобиас, не могли бы вы добавить слот в класс Worker и соединить его с собственным сигналом finished, просто чтобы проверить, испускается ли сигнал (поместите qDebug << "..." внутри слота). - person p-a-o-l-o; 04.04.2018
comment
Попробуйте, пожалуйста, сделать это из рабочего потока с нулевым первым аргументом. QTimer::singleShot(0, qApp, SLOT(quit())); - person Dmitry Sazonov; 04.04.2018