qt не вызывается слот в другом потоке

230
16 декабря 2017, 06:57

Имеется код:

main.cpp:

#include "main.h"
int main(int argc, char *argv[]){
    QCoreApplication a(argc, argv);

    QThread *testThread = new QThread;
    QWorker *worker= new QWorker();
    bool result;
    result = QObject::connect(testThread, &QThread::started, worker, &QWorker::process);
    qDebug()<<result;
    result = QObject::connect(worker, &QWorker::finished, testThread, &QThread::quit);
    qDebug()<<result;
    result = QObject::connect(worker, &QWorker::finished, testThread, &QThread::deleteLater);
    qDebug()<<result;
    result = QObject::connect(worker, &QWorker::finished, worker, &QWorker::deleteLater);

    QDataProducer* producer = new QDataProducer;
    result = QObject::connect(producer,&QDataProducer::setValue, worker,&QWorker::on_setValue);
    qDebug()<<result;
    result = QObject::connect(producer,&QDataProducer::stop, worker,&QWorker::on_stop);
    qDebug()<<result;
    worker->moveToThread(testThread);
    testThread->start();
    producer->getData();
    return a.exec();
}

main.h:

#ifndef MAIN_H
#define MAIN_H
#include <QCoreApplication>
#include <QProcess>
#include <QDebug>
#include <QThread>
class QWorker : public QObject{
    Q_OBJECT
private:
    int _value;
    bool _needExit;
public:
    QWorker()
    {
        _needExit=false;
    }
    ~QWorker(){}
public slots:
    void process(){
        while(!_needExit){
            printf("%d\n",_value);
            QThread::sleep(1);
        }
        emit finished();
    }
    void on_setValue(int val){
        _value=val;
    }
    void on_stop(){
        printf("Stop called!\n");
        _needExit=true;
    }
signals:
    void finished();
};

class QDataProducer: public QObject{
    Q_OBJECT
public:
    QDataProducer(){}
    ~QDataProducer(){}
    void getData(){
        printf("Data producing started!\n");
        for(int val=0;val<10;val++){
            emit setValue(val);
            QThread::sleep(1);
        }
        printf("Stop emmited!\n");
        emit stop();
    }
public slots:
signals:
    void stop();
    void setValue(int value);
private:
};
#endif // MAIN_H

Связи между сигналами и слотами устанавливаются. В данном коде не вызываются слоты on_stop и on_setValue класса QWorker. Хотя согласно примерам из интернета, вызываться должны.Подскажите почему?

Answer 1

Чтоб worker из другого потоко получил сигнал надо либо использовать Qt::DirectConnection пятым параметром в методе connect (он у вас Qt::AutoConnection по умолчанию), либо поднимать в потоке testThread QEventLoop и запускать цикл по обработке сообщений QEventLoop::exec(...) чтоб диспетчеризировать сигналы для объектов которые "живут" в этом потоке. Именно такой QEventLoop запускается в главном потоке внутри QApplication::exec(...)

Answer 2

Отвечу вопросом на вопрос. А сколько ждали? Предполагаю что меньше 10 секунд. Метод QDataProducer::getData "стопорит" главный поток на 10 секунд, а вместе с ним и цикл обработки событий. Пока этот метод выполняется сигналы из одного потока в другой не ходят, а накапливаются в очереди.

Костыльное решение, чтобы проверить мое предположение:

void getData(){
    printf("Data producing started!\n");
    for(int val=0;val<10;val++){
        qApp->processEvents(); //<-------------Запускаем обработку событий принудительно
        emit setValue(val);
        QThread::sleep(1);
    }
    printf("Stop emmited!\n");
    emit stop();
}

Разумеется в таком виде оставлять все нельзя. Придется все адекватно переделать. Как вариант, провести с QDataProducer те же манипуляции что и с QWorker, чтобы он тоже работал в отдельном потоке и не стопорил главный.

P.S. Сдался вам этот QThread? Чтобы его использовать нужно столько лишних телодвижений делать. Многопоточность сама по себе тема весьма сложная, а тут еще и с инструментом приходится бороться. Вот для примера как запустить метод в отдельном потоке при помощи QThreadPool:

class RunProcess : public QRunnable{
    QWorker *_worker;
public:
    RunProcess(QWorker *worker):
        _worker(worker)
    {}
    void run(){
        _worker->process();
    }
};
//...
QThreadPool::globalInstance()->start(new RunProcess(worker));

Но даже это решение может показаться многословным по сравнению QtConcurrent:

QtConcurrent::run(worker, &QWorker::process);

ИМХО можно привести следующие аналогии:

  • QThread - острое и прочное лезвие без рукоятки. Да оно острое, да им можно разрезать все что угодно. Но придется быть очень аккуратным.
  • QThreadPool - Обычный нож. В меру удобный, достаточно практичный.
  • QtConcurent - Кухонный комбайн. Супер удобный. Сделает все за вас. Но как им почистить яблоко?

P.P.S. Если вы не разработчик Qt, перестаньте использовать приписку Q для своих классов. Это сбивает с толку. Я даже попытался загуглить QDataProducer. Эту букву застолбили авторы Qt потому что на момент начала разработки были какие-то трудности с нэймспэйсами, и это был единственный способ показать что класс относится к библиотеке Qt.

P.P.P.S. printf в C++ это не есть хорошо. Используйте std::cout или QTextStream. Они удобнее, у них есть типовая безопасность, их проще рефакторить.

Answer 3

Я так понимаю, цель стоит раз в указанное время делать определенное действие. Для этого случая подход неправильный - надо в объекте воркере делать слот, реализующий одно действие, делать таймер, к сигналу timeout таймера цеплять слот с действием, а при старте воркера просто запускать таймер.

По заданному вопрос - почему не вызываются слоты, в комментариях уже ответили, поток занят функцией process и сигналы встают в очередь и не обрабатываются. Руками теребонькать processEvents крайне нежелательно.

READ ALSO
D3D_MIN_PRECISION не определён

D3D_MIN_PRECISION не определён

Пишу приложение с использованием DirectX SDK на C++Всё было нормально до того момента как я стал писать поддержку шейдеров в своём приложении

191
QEventLoop останавливает загрузку страницы

QEventLoop останавливает загрузку страницы

Пишу приложение для просмотра расписания автобусов в городеРасписание беру с сайта: downloader_

204
Проблема с поиском элемента в списке

Проблема с поиском элемента в списке

Существует шаблон списка:

190
использовать функции из linux .so

использовать функции из linux .so

Линкуюso библиотеку динамически

261