I am using Qt in order to write a GUI application.
A main thread is responsible for the GUI and creates an QThread in order to do some work with an object.
class Worker
{
void start() {
QTimer* timer = new Timer();
connect(timer,SIGNAL(timeout()),this,SLOT(do()));
}
void do() {
//do some stuff
emit finished();
}
}
class GUI
{
//do some GUI work then call startWorker();
void startWorker() {
QThread* thread = new Thread();
Worker* worker = new Worker();
worker->moveToThread(thread);
connect(thread, SIGNAL(started()), worker, SLOT(start()));
connect(worker, SIGNAL(finished()), workerthread, SLOT(quit()));
connect(worker, SIGNAL(finished()), worker, SLOT(deleteLater()));
}
}
Now I have several problems:
The timer in my worker class does not work. Maybe it is because the new thread has no event loop, but I have no idea how to create such one. I tried
connect(workerthread, SIGNAL(started()), workerthread, SLOT(exec()));
but it does not work either.
When I try to wait on the new thread, the signal is never sent
class GUI
{
void exit() {
thread->wait();
}
}
I think it also is because there is no event loop and because of that no signal is emitted.
Does anybody have an idea how to solve these problems?
why not use qthreadpool, than you make your task class inherits from qrunnable and qobject, this way you can use signals and slots to pass data from one thread to another, is much simpler to implement, and increase performance from not recreating a thread or having one sleeping all the time
class myTask : public QObject, public QRunnable{
Q_OBJECT
protected:
void run(); //where you actually implement what is supposed to do
signals:
void done(int data);//change int to whatever data type you need
}
//moc click example, or use a timer to call this function every x amount of time
void button_click(){
myTask *task = new myTask();
task->setAutoDelete(true);
connect(task,SIGNAL(done(int)),this,SLOT(after_done(int)),Qt::QueuedConnection);
QThreadPool::globalInstance()->start(task);
}
by default you application gets 1 thread automatically, which you can use to handle the graphic, than use the qthreadpool to process the data/object on demand, you can even set the max amount of threads your application can use to process new request, the others will stay in a queue until one thread is freed
QThreadPool::globalInstance()->setMaxThreadCount(5);
this is a sample code for you :
QThread* thread = new QThread();
Worker* worker = new Worker(3000);
worker->moveToThread(thread);
QObject::connect(thread, SIGNAL(started()), worker, SLOT(start()));
thread->start();`
class Worker : public QObject
{
Q_OBJECT
public:
explicit Worker(qint32,QObject *parent = 0);
qint32 myTime;
signals:
void workFinished();
public slots:
void doWork();
void start();
private:
QTimer *timer;
};
#include "worker.h"
#include <QTimer>
#include <QDebug>
Worker::Worker(qint32 t,QObject *parent) :
QObject(parent)
{
myTime=t;
}
void Worker::start()
{
timer = new QTimer();
timer->start(myTime);
qDebug()<<QString("start work in time:%1").arg(myTime);
connect(timer,SIGNAL(timeout()),this,SLOT(doWork()));
}
void Worker::doWork()
{
qDebug()<<"dowork";
timer->stop();
emit workFinished();
}
Debug results :
start work in time:3000
I hope this helps you.
Related
QThread documentation suggests two ways to make code run in a separate thread. If I use moveToThread approach, I have to call processEvents() to issue the timeouts, to have the lambda executed. And this seems to cost a lot of CPU. Why is so?
class Worker : public QObject
{
Q_OBJECT
QTimer* timer;
bool m_abort = false;
public:
Worker() {}
void abort() {m_abort = true;}
public slots:
void run() {
timer = new QTimer;
connect(timer, &QTimer::timeout, []{qDebug() << "computed";});
timer->start(1000);
forever {
if (m_abort) break;
QCoreApplication::processEvents();
}
}
};
class MainWidget : public QWidget
{
Q_OBJECT
QThread thread;
Worker* worker;
public:
MainWidget()
{
worker = new Worker;
worker->moveToThread(&thread);
connect(this, &MainWidget::start, worker, &Worker::run);
thread.start();
emit start();
}
~MainWidget(){worker->abort(); thread.quit(); thread.wait();}
signals:
void start();
};
However if I subclass QThread and reimplement run() it's not necessary to call processEvents. And CPU cost seems lower. Why?
class Worker : public QThread
{
public:
Worker() {}
protected:
void run() override {
QTimer timer;
connect(&timer, &QTimer::timeout, []{qDebug() << "computed";});
timer.start(1000);
exec();
}
};
class MainWidget : public QWidget
{
Q_OBJECT
Worker* worker;
public:
MainWidget()
{
worker = new Worker;
worker->start();
}
};
your run() function 'blocks' the thread. It is being invoked in the thread context, but never returns. This means, the event loop in the thread doesn't get executed anymore as soon as your run() funtion is called.
For the timer events to call your lambdas, the event loop has to be processed.
If you would modify your run function like this:
void run() {
timer = new QTimer(this);
connect(timer, &QTimer::timeout, []{qDebug() << "computed";});
timer->start(1000);
// don't loop here, instead exit the function
// and let the thread return back to the event loop
}
then your lambdas should get called. The thread will also keep running until you call thread.quit()
note: you can also connect directly to the '''started''' signal of the thread:
connect(&thread, &QThread::started, worker, &Worker::run);
thread.start();
moveToThread approach might be improved by calling run() function just after thread emit started.
But I still don't know why the way I put it initially doesn't work.
I'm now making a program that has to manage lots of QWidgets created when receiving requests, but I can't figure out how to create widgets in the QObject class. The compiler complains that "QObject: Cannot create children for a parent that is in a different thread."
Having on Google for an hour, I've tried many ways to solve the problem(here, here, and here), but none of them worked.
Here is some code about it:
// OSD.hpp
#ifndef OSD_HPP
#define OSD_HPP
#include <QWidget>
#include <QLabel>
#include <QVBoxLayout>
class OSD : public QWidget {
Q_OBJECT
public:
explicit OSD(QWidget *parent = nullptr);
void setText(QString);
const QString getText() const;
private:
QLabel *text;
QVBoxLayout *layout1;
};
#endif // OSD_HPP
// Teller.hpp
#ifndef Teller_HPP
#define Teller_HPP
#include "OSD.hpp"
#include <QObject>
class Teller : public QObject {
Q_OBJECT
public:
explicit Teller(int port, QObject *parent = nullptr);
void SpawnNotification(std::string);
~Teller();
};
#endif // Teller_HPP
// Teller.cpp
class Worker : public QObject {
Q_OBJECT
OSD *o = nullptr;
public:
Worker(){};
~Worker() {
if (o) {
o->deleteLater();
}
};
public slots:
void process() {
o = new OSD; // Problem here
o->setText(QString("Hello"));
o->show();
QThread::sleep(1);
emit finished();
}
signals:
void finished();
};
void Teller::SpawnNotification(std::string){
QThread *thread = new QThread;
Worker *worker = new Worker();
worker->moveToThread(thread);
connect(worker, &Worker::finished, worker, &Worker::deleteLater);
connect(worker, &Worker::finished, thread, &QThread::deleteLater);
connect(thread, &QThread::started, worker, &Worker::process);
thread->start();
}
Am I missing something important?
In Qt the GUI lives in the main-thread. The Qt documentation says this:
GUI Thread and Worker Thread
As mentioned, each program has one thread when it is started. This thread is called the "main thread" (also known as the "GUI thread" in Qt applications). The Qt GUI must run in this thread. All widgets and several related classes, for example QPixmap, don't work in secondary threads. A secondary thread is commonly referred to as a "worker thread" because it is used to offload processing work from the main thread.
(Link: https://doc-snapshots.qt.io/qt5-5.12/thread-basics.html)
You would need to create the widgets on the main-thread and connect the worker's signals to the widget's slots. Similar to this (not tested..):
// Must be called in main thread
void Teller::SpawnNotification(std::string){
QThread *thread = new QThread;
QWidget *widget = new QWidget(nullptr, Qt::WA_DeleteOnClose); // Top-Level window
Worker *worker = new Worker();
worker->moveToThread(thread);
connect(worker, &Worker::finished, worker, &Worker::deleteLater);
connect(worker, &Worker::finished, thread, &QThread::deleteLater);
connect(worker, &Worker::finished, widget, &QWidget::show);
//connect(worker, &Worker::dataReady, widget, &QWidget::setData); // TODO
connect(thread, &QThread::started, worker, &Worker::process);
thread->start();
}
Edit (see comment below or https://doc.qt.io/qt-5/qt.html#ConnectionType-enum):
Also: Qt needs to know that the connection goes across different threads. Therefore you have to do the connection after moving "worker" to the new thread or explicitly use Qt::QueuedConnection.
Hope that helps!
Currently I have two classes that look something like this:
class Worker : public QObject
{
Q_OBJECT
bool aborted = false;
public:
Worker() : QObject() {}
public slots:
void abort() { aborted = true; }
void doWork()
{
while(!aborted && !work_finished)
{
//do work
QCoreApplication::processEvents();
}
}
};
class Controller : public QObject
{
Q_OBJECT
QThread workerThread;
public:
Controller() : QObject()
{
Worker *worker = new Worker;
worker->moveToThread(&workerThread);
connect(&workerThread, &QThread::finished, worker, &Worker::deleteLater);
connect(this, &Controller::startWork, worker, &Worker::doWork);
connect(this, &Controller::aborted, worker, &Worker::abort);
}
signals:
void startWork();
void aborted();
};
Controller *cont = new Controller;
emit cont->startWork(); // Start the loop
emit cont->aborted(); // Stop the loop
So the idea is that there is a loop running in a Worker thread, which can be stopped from a Controller thread.
In the example this is done by calling QCoreApplication::processEvents(), which allows signals to call slots before returning control to the loop.
It's important the loop is only stopped at the start or end of an iteration.
Although this works nicely, I think QCoreApplication::processEvents() is pretty expensive, at least when used inside a very long loop (up to thousands in practice).
So my question is, how can I achieve the same result in a better/cheaper way?
There are three alternative solutions that I'm aware of at this time.
1. QThread::requestInterruption (suggested by #Felix)
According to QThread::isInterruptionRequested:
Take care not to call it too often, to keep the overhead low.
Whereas QCoreApplication::processEvents makes no remark on performance or memory usage, so I don't think QThread::requestInterruption is an improvement over QCoreApplication::processEvents in this case.
2. std::atomic (suggested by #Felix)
The main characteristic of atomic objects is that access to this contained value from different threads cannot cause data races [...]
The boolean can be stored inside a std::atomic which can be made a member of the Controller class instead of the Worker class. Then we need to pass a reference to aborted to and store it in Worker, and set it to true from Controller when needed.
I didn't fully test this approach, so please correct me if I got something wrong.
class Worker : public QObject {
Q_OBJECT
std::atomic<bool> &aborted;
public:
Worker(std::atomic<bool> &aborted) : QObject(), aborted(aborted) {}
public slots:
void doWork() {
while(!aborted.load() && !work_finished) /* do work */
}
};
class Controller : public QObject {
Q_OBJECT
QThread workerThread;
std::atomic<bool> aborted;
public:
Controller() : QObject() {
aborted.store(false);
Worker *worker = new Worker(aborted);
worker->moveToThread(&workerThread);
connect(&workerThread, &QThread::finished, worker, &Worker::deleteLater);
connect(this, &Controller::startWork, worker, &Worker::doWork);
connect(this, &Controller::aborted, worker, &Worker::abort);
}
void abort() { aborted.store(true); }
signals:
void startWork();
};
Controller *cont = new Controller;
emit cont->startWork(); // Start the loop
cont->abort(); // Stop the loop
3. QWaitCondition & QMutex
A boolean paused will be needed. Controller and Worker need read/write access to it.
Set paused to true in Controller when needed.
During the loop in Worker, if(paused): QWaitCondition::wait() until QWaitCondition::wakeAll() is called from the calling thread.
QMutex::lock will need to be called whenever paused is accessed.
class Worker : public QObject {
Q_OBJECT
bool &aborted, &paused;
QWaitCondition &waitCond;
QMutex &mutex;
public:
Worker(bool &aborted, bool &paused, QWaitCondition &waitCond, QQMutex &mutex)
: QObject(), aborted(aborted), paused(paused), waitCond(waitCond), mutex(mutex) {}
public slots:
void doWork() {
while(!aborted && !work_finished) {
//do work
mutex.lock();
if(paused) {
waitCond.wait(&mutex);
paused = false;
}
mutex.unlock();
}
}
void abort() { aborted = true; }
};
class Controller : public QObject {
Q_OBJECT
bool aborted=false, paused=false;
QWaitCondition waitCond;
QMutex mutex;
QThread workerThread;
public:
Controller() : QObject() {
Worker *worker = new Worker(aborted, paused, waitCond, mutex);
worker->moveToThread(&workerThread);
connect(&workerThread, &QThread::finished, worker, &Worker::deleteLater);
connect(this, &Controller::startWork, worker, &Worker::doWork);
}
void abort() {
mutex.lock();
paused = true; // Worker starts waiting
mutex.unlock();
if(confirmed_by_user) aborted = true; // Don't need to lock because Worker is waiting
waitCond.wakeAll(); // Worker resumes loop
}
signals:
void startWork();
};
Controller *cont = new Controller();
emit cont->startWork(); // Start the loop
cont->abort(); // Stop the loop
I have two classes:A and B,both of them subclass from the QObject.
class A:public QObject
{
Q_OBJECT
public:
A();
~A();
}
i just referred to this article.
Then in the GUI (say the main Thread)I try to exchange data between A and B in different threads using
pA = new A();
QThread *workerThread = new QThread;
pA->moveToThread(workerThread);
connect(workerThread , SIGNAL(started()), pA, SLOT(doWork()));
connect(pA, SIGNAL(finished()), workerThread , SLOT(quit()));
connect(pA, SIGNAL(finished()), pA, SLOT(deleteLater()));
connect(workerThread , SIGNAL(finished()), workerThread , SLOT(deleteLater()));
workerThread ->start();
pB = new B();
connect(pA,SIGNAL(sigProduce(double)),pB,SLOT(slotConsume(double)));//I just don't know where to put this line
QThread *workerThread = new QThread;
pB ->moveToThread(workerThread);
connect(workerThread , SIGNAL(started()), pB , SLOT(doWork()));
connect(pB , SIGNAL(finished()), workerThread , SLOT(quit()));
connect(pB , SIGNAL(finished()), pB , SLOT(deleteLater()));
connect(workerThread , SIGNAL(finished()), workerThread , SLOT(deleteLater()));
workerThread ->start();
sigProduce(double) is a signal defined in the A object and slotConsume(double) is a public slot:
header of class B:
public slots:
void slotConsume(double);
so after the program started obj B can not get the double data from obj A.Does that connect(pA,pB) code's position matters?can anyone help me?THANKS.
Before I go into the details how to solve your problem, make sure the problem is valid in the first place. If the only reason you're using threads is because you want an infinite loop, then maybe you don't need threads at all. If the work inside the loop is light, then you can just do it in the main thread with a QTimer. Read this article to know more about this.
Anyway the problem you have is because you have an infinite loop in your doWork method, which is blocking the thread's event loop. Because of this you had to resort to calling processEvents. However this is not a good design. A better way would be to let the thread go back to the event loop by removing the infinite loop from doWork.
You can then simulate a loop with a timer, and stop the timer when you want to stop the thread. Or you can use QMetaObject::invokeMethod with a Qt::QueuedConnection to call the doWork method from the event loop, and use a bool condition top stop the work. In this example I used the invokeMethod option:
#include <QCoreApplication>
#include <QThread>
#include <QTimer>
#include <QDebug>
class Worker : public QObject
{
Q_OBJECT
public:
Worker(QObject *parent = 0) : QObject(parent), _stop(false) {}
public slots:
void doWork()
{
if(_stop)
return;
QThread::sleep(1); // simulating some heavy work here
emit calculationComplete(1564);
QMetaObject::invokeMethod(this, "doWork", Qt::QueuedConnection);
}
void stop()
{
_stop = true;
}
signals:
void calculationComplete(int result);
private:
bool _stop;
};
class ResultHandler : public QObject
{
Q_OBJECT
public:
ResultHandler(QObject *parent = 0) : QObject(parent) {}
public slots:
void handleResult(int result)
{
// do something with result
qDebug() << Q_FUNC_INFO << "Result:" << result;
}
};
int main(int argc, char *argv[])
{
QCoreApplication a(argc, argv);
QThread thread_a;
Worker worker;
ResultHandler handler;
QObject::connect(&worker, &Worker::calculationComplete, &handler, &ResultHandler::handleResult);
QObject::connect(&thread_a, &QThread::started, &worker, &Worker::doWork);
QObject::connect(&thread_a, &QThread::finished, &worker, &Worker::stop);
worker.moveToThread(&thread_a);
thread_a.start();
QTimer::singleShot(5000, &thread_a, &QThread::quit); // just simulating an exit
int result = a.exec();
thread_a.quit();
thread_a.wait(5000);
return result;
}
#include "main.moc"
I have a problem with signal/slots in a QThread class. My design looks like this:
class Manager : public QObject {
Q_OBJECT
public:
Manager(QObject* parent) : QObject(parent) {
Thread thread* = new Thread(this);
connect(this, SIGNAL(testsignal()), thread, SLOT(test()));
thread->start();
...
emit testsignal();
}
signals:
void testsignal();
};
class Thread : public QThread {
Q_OBJECT
public slots:
void test() {
qDebug() << "TEST";
}
private:
void run() {}
};
The signal never reaches my test() method. Can someone help? Thanks.
The problem is that sending signals across threads results in queuing the signal into the target thread's event queue (a queued connection). If that thread never processes events, it'll never get the signal.
Also, according to the QThread::run documentation:
Returning from this method will end the execution of the thread.
In other words, having an empty run method results in instant termination of the thread, so you're sending a signal to a dead thread.
Signals sent to a QThread object will go to the thread of the parent object. In this case to the same thread that created it.
To have a object live on another thread you should move it to that thread:
class Manager : public QObject {
Q_OBJECT
public:
Manager(QObject* parent) : QObject(parent) {
Thread thread* = new QThread(this);
Receiver* rec = new Receiver(); //no parent
connect(this, SIGNAL(testsignal()), rec, SLOT(test()));
connect(thread, SIGNAL(finished()), rec, SLOT(deleteLater()));
rec->moveToThread(thread);
thread->start();
...
emit testsignal();
}
signals:
void testsignal();
};
class Receiver: public QObject {
Q_OBJECT
public slots:
void test() {
qDebug() << "TEST";
}
};