Timer in thread's worker Qt - c++

I'm trying to use timer in worker class. This is my Worker class:
Worker.h
class Worker : public QObject
{
Q_OBJECT
public:
explicit Worker(QObject *parent = nullptr);
signals:
void finished(void);
public slots:
void process(void);
void test(void);
private:
QMutex m_mutex;
};
Worker.cpp
void Worker::process(void)
{
qDebug() << "worker process"; //This works
QTimer *timer = new QTimer(this);
connect(timer, SIGNAL(timeout()), this, SLOT(test()));
forever
{
}
}
void Worker::test(void)
{
qDebug() << "test123"; //This does not work
}
I start this worker class in new thread:
QThread *thread = new QThread;
Worker *worker = new Worker;
worker->moveToThread(thread);
QObject::connect(thread, SIGNAL(started()), worker, SLOT(process()), Qt::QueuedConnection);
QObject::connect(thread, SIGNAL(finished()), thread, SLOT(deleteLater()));
QObject::connect(worker, SIGNAL(finished()), thread, SLOT(quit()));
QObject::connect(worker, SIGNAL(finished()), worker, SLOT(deleteLater()));
thread->start();
The problem is that timer from Worker::process does not work. Do I need to initialize this timer in new thread in special way?

You need to call void QTimer::start(int msec) or void QTimer::start() after you create the timer. You also don't need "forever" in your process() slot.
Try this instead:
void Worker::process(void)
{
qDebug() << "worker process"; //This works
QTimer *timer = new QTimer(this);
connect(timer, SIGNAL(timeout()), this, SLOT(test()));
timer->start(1000); // Fire timer timeout each 1000ms.
}

Related

Is the timer being started from another thread?

QThread documentation suggests two ways to make code run in a separate thread. If I subclass QThread and reimplement run(), then I get
QBasicTimer::start: Timers cannot be started from another thread
-
#include <QWidget>
#include <QThread>
#include <QBasicTimer>
#include <QDebug>
#include <QEvent>
#include <QCoreApplication>
class Worker : public QThread
{
Q_OBJECT
int id;
bool m_abort = false;
bool compute = false;
public:
Worker() {}
protected:
void timerEvent(QTimerEvent *event) override {
if (event->timerId() == id) {
compute = true;
} else {
QObject::timerEvent(event);
}
}
public slots:
void abort() {m_abort = true;}
void run() {
qDebug() << QThread::currentThreadId();
QBasicTimer timer;
id = timer.timerId();
timer.start(1000, this);
forever {
if (m_abort) break;
QCoreApplication::processEvents();
if (compute)
qDebug() << "computed";
compute = false;
}
}
};
class MainWidget : public QWidget
{
Q_OBJECT
QThread thread;
Worker* worker;
public:
MainWidget()
{
qDebug() << QThread::currentThreadId();
worker = new Worker;
worker->start();
}
~MainWidget(){worker->abort();}
};
1) Is the timer being started from another thread?
2) Why I don't get that warning when QBasicTimer is replaced by QTimer?
3) Why I don't get that warning when using moveToThread?
#include <QWidget>
#include <QThread>
#include <QBasicTimer>
#include <QDebug>
#include <QEvent>
#include <QCoreApplication>
class Worker : public QObject
{
Q_OBJECT
QBasicTimer* timer;
bool m_abort = false;
bool compute = false;
public:
Worker() {}
protected:
void timerEvent(QTimerEvent *event) override {
if (event->timerId() == timer->timerId()) {
compute = true;
} else {
QObject::timerEvent(event);
}
}
public slots:
void abort() {m_abort = true;}
void run() {
timer = new QBasicTimer;
timer->start(1000, this);
forever {
if (m_abort) break;
QCoreApplication::processEvents();
if (compute)
qDebug() << "computed";
compute = false;
}
}
};
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();
};
Regarding the first (non-moveToThread) example...
A quick look at the Qt source for QBasicTimer::start shows the following...
void QBasicTimer::start(int msec, QObject *obj)
{
QAbstractEventDispatcher *eventDispatcher = QAbstractEventDispatcher::instance();
// ...
if (Q_UNLIKELY(obj && obj->thread() != eventDispatcher->thread())) {
qWarning("QBasicTimer::start: Timers cannot be started from another thread");
return;
}
So it expects its second argument obj to have a thread affinity equal to the current thread.
In your Worker::run implementation, however, you have...
timer.start(1000, this);
In this context the current thread is the new thread created by the QThread instance but this refers to the QWorker instance created by the MainWidget on the main GUI thread. Hence the warning.
Edit 1:
To the question...
why it works with moveToThread()?
Consider the implementation of the MainWidget ctor...
MainWidget()
{
worker = new Worker;
worker->moveToThread(&thread);
connect(this, &MainWidget::start, worker, &Worker::run);
thread.start();
emit start();
}
By the time Worker::run is called the Worker instance has been moved to the new thread. So when the line...
timer.start(1000, this);
executes, this (which refers to the Worker instance) is on the current thread and the thread affinity test in QBasicTimer::start passes without warning.
Sorry if the above is a bit convoluted but the important thing is to consider the thread affinity of the second arg to QBasicTimer::start: it must be the currently running thread.

How create paralles threads in Qt?

I created a Worker class for manager threads in Qt.
#include <QThread>
#include <QProcess>
#include <QObject>
class Worker_Bis : public QObject
{
Q_OBJECT
public:
Worker_Bis() : flag_running(false), flag_stopping(false){
thread = new QThread();
moveToThread(thread);
connect(thread, SIGNAL(started()), this, SLOT(start()));
connect(thread, SIGNAL(terminated()), this, SLOT(stop()));
connect(this, SIGNAL(finished()), thread, SLOT(quit()));
connect(this, SIGNAL(terminated()), thread, SLOT(quit()));
connect(thread, SIGNAL(finished()), this, SLOT(deleteLater()));
thread->start();
}
~Worker_Bis(){
if (!isRunning())
thread->deleteLater();
flag_stopping = true;
while (isRunning()){}
deleteLater();
}
bool isRunning() { return flag_running; };
bool isStopping() { return flag_stopping; };
virtual int getLoops() { return 1; };
public slots:
void start(){
flag_running = true;
flag_stopping = false;
starting();
emit started();
int exit;
for (int loop = 0; loop < getLoops(); ++loop){
if (isStopping()){
exit = -1;
break;
}
emit startLoop(loop);
exit = exec(loop);
if (exit != 0)
break;
emit finishLoop(loop);
}
finishing(exit);
if (exit == -1)
emit terminated();
else{
emit finished();
if (exit != 0)
emit error(exit);
}
flag_running = false;
};
void stop(){
flag_stopping = true;
stopping();
emit stopped();
};
signals:
void started();
void finished();
void stopped();
void terminated();
void error(int code);
void startLoop(int loop);
void finishLoop(int loop);
protected:
QThread* thread;
bool flag_stopping;
bool flag_running;
virtual void starting() {};
virtual int exec(int loop) { return 0; };
virtual void finishing(int) {};
virtual void stopping() {};
};
And I implemented twos buttons: start and stop. I start a thread clicking in start button and I stop thread clicking in stop button.
void ButtonStartClicked(){
worker = new Worker(myObj);
}
void ButtonStopClicked(){
delete worker;
}
If I click in start button many times, I will get parallel threads. Can I obtain some memory problems clicking in start button many times?

Is it possible for a worker to stop it's own thread with signal/slot connection in Qt?

I'm trying to implement threaded worker for parallel computation. The problem I encountered is that the quit() slot of thread is not triggered, therefore applications waits at while(thread->isRunning()). Is it possible to stop thread from worker using signal-slot connection between them?
Here is my code:
main.cpp:
#include <QCoreApplication>
#include "workermanager.h"
int main(int argc, char *argv[])
{
QCoreApplication a(argc, argv);
WorkerManager workerManager;
workerManager.process();
return a.exec();
}
worker.h:
#include <QObject>
#include <QDebug>
class Worker : public QObject
{
Q_OBJECT
public:
explicit Worker(QObject *parent = 0) :
QObject(parent){}
signals:
void processingFinished();
public slots:
void process()
{
qDebug() << "processing";
emit this->processingFinished();
}
};
workermanager.h:
#include "worker.h"
#include <QThread>
class WorkerManager : public QObject
{
Q_OBJECT
public:
explicit WorkerManager(QObject *parent = 0) :
QObject(parent){}
void process()
{
QThread* thread = new QThread;
Worker* worker = new Worker;
connect(thread,SIGNAL(started()),worker,SLOT(process()));
connect(worker,SIGNAL(processingFinished()),thread,SLOT(quit()));
worker->moveToThread(thread);
thread->start();
qDebug() << "thread started";
while(thread->isRunning())
{
}
qDebug() << "thread finished";
//further operations - e.g. data collection from workers etc.
}
};
That while loop blocks your thread and prevents it from processing any signals, if you really want to wait for the thread to finish, you need to start an event loop.
Try this instead:
void process()
{
QThread* thread = new QThread;
Worker* worker = new Worker;
connect(thread,SIGNAL(started()),worker,SLOT(process()));
connect(worker,SIGNAL(processingFinished()),thread,SLOT(quit()));
worker->moveToThread(thread);
QEventLoop loop;
connect(thread, &QThread::finished, &loop, &QEventLoop::quit);
qDebug() << "thread started";
thread->start();
loop.exec();
qDebug() << "thread finished";
//further operations - e.g. data collection from workers etc.
}
But I have to admit I don't really see the point of this code. If you need to wait for the task to finish to keep doing stuff, you might as well run it directly.
There at least your program will be able to process other things (if there are some) in the mean time.

creating new thread using Qthread-Qt5

I am trying to create a new thread gpsthread which should run in the back ground, and store the value.
class gpsthread: public QThread{
Q_OBJECT
private:nrega_status_t status2;
public:
explicit gpsthread(QObject *parent = 0):QThread(parent) {
// QTimer *t = new QTimer(this);
// connect(t, SIGNAL(timeout()), this, SLOT(processgps()));
// t->start(10000);
}
void run(){
qDebug()<<"inside gps thread\n";
QTimer *t = new QTimer(this);
connect(t, SIGNAL(timeout()), this, SLOT(processgps()));
t->start(10000);
}
public slots:void processgps(){
int status2;
status2=gps_management();
}
};
My main class is quickview.
int main(int argc, char *argv[])
{
QString file = "qml/main.qml";
QApplication app(argc, argv);
TranslationTest myObj;
QuickView view;
subthread object;
gpsthread obj;
gprsthread gprs;
view.rootContext()->setContextProperty("rootItem", (QObject *)&myObj);
obj.start();
//from subthread
QObject::connect(&object, SIGNAL(batterytoqml(QVariant,QVariant)),item, SLOT(frombattery(QVariant,QVariant)));
QObject::connect(&gprs, SIGNAL(gprstoqml(QVariant)),item, SLOT(fromgprs(QVariant)));
return app.exec();
}
I have tried this as well
class gpsthread: public QThread{
Q_OBJECT
private:nrega_status_t status2;
public:QTimer* t;
explicit gpsthread(QObject *parent = 0):QThread(parent) {
// QTimer *t = new QTimer(this);
// connect(t, SIGNAL(timeout()), this, SLOT(processgps()));
// t->start(10000);
}
void run(){
qDebug()<<"inside gps thread\n";
t = new QTimer(this);
connect(t, SIGNAL(timeout()), this, SLOT(processgps()));
t->start(10000);
exec();
}
public slots:void processgps(){
int status2;
status2=gps_management();
}
};
But it is giving error saying
QObject: Cannot create children for a parent that is in a different thread
If I create object in constructor then also it will give the same error because the object will be created in the main thread.
How to resolve this?
Inheriting from QThread is not the recommended usage. QThread is a complete class that runs an event loop, which is generally what you need. The documentation recommends using a worker object that inherits from QObject and does work in a slot. The worker is moved into a QThread. When a connected signal is sent, the slot will run in the correct thread.
class gpsworker: public QObject
{
Q_OBJECT
public:
explicit gpsworker(QObject *parent = 0):
QObject(parent)
{}
public slots:
void processgps() {
qDebug() << "processgps()" << QThread::currentThreadId();
}
}
void OwnerClass::startWorker() {
QTimer *timer = new QTimer(this);
QThread *thread = new QThread(this);
this->worker = new gpsworker();
this->worker->moveToThread(thread);
connect(timer, SIGNAL(timeout()), this->worker, SLOT(processgps()) );
connect(thread, SIGNAL(finished()), this->worker, SLOT(deleteLater()) );
thread->start();
timer->start();
}
If you want the timer to live in the other thread as well, QTimer::start is a slot.
QObject: Cannot create children for a parent that is in a different thready
is beacuse t = new QTimer(this) in the run() is creating an object in the child thread but this which point at gpsthread is in the main thread. A simple solution is t = new QTimer() without a parent and delete the timer in the destructor. Here is an example:
class gpsthread : public QThread {
Q_OBJECT
public:
explicit gpsthread(QObject *parent = 0):
QThread(parent)
,timer(NULL) {
qDebug() << "Parent thread" << QThread::currentThreadId();
}
~gpsthread() {
quit();
wait();
delete timer;
}
protected:
void run() {
qDebug() << "Inside gps thread" << QThread::currentThreadId();
timer = new QTimer; // no parent
connect(timer, SIGNAL(timeout()), this, SLOT(processgps()));
timer->start(1000);
exec();
}
public slots:
void processgps() {
qDebug() << "processgps()" << QThread::currentThreadId();
}
private:
QTimer *timer;
};
Soon you will find out console print:
Parent thread 0x3b28
inside gps thread 0x3f10
processgps() 0x3b28
processgps() 0x3b28
processgps() 0x3b28
processgps() 0x3b28
which means processgps() is not working in your child thread. This is because this slot is a member of gpsthread which is in the main thread. A simple solution is call processgps() directly and use sleep() as timer:
class gpsthread : public QThread
{
Q_OBJECT
public:
explicit gpsthread(QObject *parent = 0):
QThread(parent)
, abort(false) {
qDebug() << "Parent thread" << QThread::currentThreadId();
}
~gpsthread() {
abort = true;
wait();
}
protected:
void run() {
while(!abort) {
sleep(1);
processgps();
}
}
public slots:
void processgps() {
qDebug() << "processgps()" << QThread::currentThreadId();
}
private:
bool abort;
};
This is not a nice solution, a recommanded way is creating a worker to do all the job and then use QObject::moveToThread() as said in QThread Document

QThread - Call a method of another thread

I'm trying to call a method of another thread (not the Main), using invokeMethod. Unfortunately that makes the application crash as soon as it tries to execute the invokeMethod!
Am I mistaking something?
// main.cpp
#include <QtCore>
#include "entrypointclass.h"
int main(int argc, char *argv[])
{
QCoreApplication a(argc, argv);
qDebug() << a.thread()->currentThreadId() << " - Application started.";
EntryPointClass entryPoint;
entryPoint.runInNewThread();
return a.exec();
}
// Entrypoint.h
#ifndef ENTRYPOINTCLASS_H
#define ENTRYPOINTCLASS_H
#include "worker.h"
#include <QtCore>
class EntryPointClass : public QObject
{
Q_OBJECT
public:
EntryPointClass();
~EntryPointClass();
void runInNewThread();
public slots:
void timeoutExpired();
private:
Worker* m_Worker;
QThread* m_Thread;
};
#endif // ENTRYPOINTCLASS_H
// Entrypoint.cpp
#include <QTCore>
#include "entrypointclass.h"
#include "Worker.h"
EntryPointClass::EntryPointClass()
{
qDebug() << "EntryPointClass created";
}
EntryPointClass::~EntryPointClass()
{
qDebug() << "EntryPointClass destroyed";
}
void EntryPointClass::runInNewThread()
{
QThread* m_Thread = new QThread;
Worker* m_Worker = new Worker();
connect(m_Thread, SIGNAL(started()), m_Worker, SLOT(doSomething()));
connect(m_Worker, SIGNAL(finished()), m_Thread, SLOT(quit()));
connect(m_Thread, SIGNAL(finished()), m_Thread, SLOT(deleteLater()));
connect(m_Thread, SIGNAL(finished()), m_Worker, SLOT(deleteLater()));
QTimer* timer = new QTimer;
timer->setSingleShot(true);
//bool bOK = connect(timer, SIGNAL(timeout()), m_Worker, SLOT(closeWorker()), Qt::BlockingQueuedConnection);
connect(timer, SIGNAL(timeout()), this, SLOT(timeoutExpired()));
m_Worker->moveToThread(m_Thread);
m_Thread->start();
timer->start(5000);
}
void EntryPointClass::timeoutExpired()
{
qDebug() << "timeout expired";
if (m_Worker != NULL)
{
QMetaObject::invokeMethod(m_Worker, "closeWorker", Qt::QueuedConnection);
}
}
// Worker.h
#ifndef WORKER_H
#define WORKER_H
#include <QtCore>
class Worker : public QObject
{
Q_OBJECT
public:
Worker();
~Worker();
public slots:
void doSomething();
void closeWorker();
private:
bool m_bAbort;
QMutex m_mutex;
signals:
void finished();
};
#endif // WORKER_H
// Worker.cpp
#include "worker.h"
#include <unistd.h>
#include "QTcore"
Worker::Worker()
: m_mutex()
{
qDebug() << this->thread()->currentThreadId() << "Worker created";
m_bAbort = false;
//qDebug() << QString("Thread %1 - Worker created").arg("");//this->thread()->currentThreadId());
}
Worker::~Worker()
{
qDebug() << this->thread()->currentThreadId() << "Worker destroyed";
}
void Worker::doSomething()
{
while(!m_bAbort)
{
sleep(2);
qDebug() << this->thread()->currentThreadId() << "Do Something!";
}
emit finished();
}
void Worker::closeWorker()
{
qDebug() << this->thread()->currentThreadId() << "closeWorker triggered!";
QMutexLocker mutexLocker(&m_mutex);
m_bAbort = true;
}
Instead of invoking a method directly, try posting a custom event. This will require that you implement an event filter at the target that handles your custom event. Event posting always works as long as an event loop is active in the target thread.
Take a look at this answer here too. Also here.
Hope this helps.