Имеется код:
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. Хотя согласно примерам из интернета, вызываться должны.Подскажите почему?
Чтоб worker из другого потоко получил сигнал надо либо использовать Qt::DirectConnection
пятым параметром в методе connect
(он у вас Qt::AutoConnection
по умолчанию), либо поднимать в потоке testThread QEventLoop
и запускать цикл по обработке сообщений QEventLoop::exec(...) чтоб диспетчеризировать сигналы для объектов которые "живут" в этом потоке. Именно такой QEventLoop запускается в главном потоке внутри QApplication::exec(...)
Отвечу вопросом на вопрос. А сколько ждали? Предполагаю что меньше 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
. Они удобнее, у них есть типовая безопасность, их проще рефакторить.
Я так понимаю, цель стоит раз в указанное время делать определенное действие. Для этого случая подход неправильный - надо в объекте воркере делать слот, реализующий одно действие, делать таймер, к сигналу timeout
таймера цеплять слот с действием, а при старте воркера просто запускать таймер.
По заданному вопрос - почему не вызываются слоты, в комментариях уже ответили, поток занят функцией process
и сигналы встают в очередь и не обрабатываются. Руками теребонькать processEvents
крайне нежелательно.
Пишу приложение с использованием DirectX SDK на C++Всё было нормально до того момента как я стал писать поддержку шейдеров в своём приложении
Пишу приложение для просмотра расписания автобусов в городеРасписание беру с сайта: downloader_