I am trying to start a QTimer in a specific thread. However, the timer does not seem to execute and nothing is printing out. Is it something to do with the timer, the slot or the thread?
main.cpp
#include "MyThread.h"
#include <iostream>
using namespace std;
int main(int argc, char *argv[]) {
MyThread t;
t.start();
while(1);
}
MyThread.h
#ifndef MYTHREAD_H
#define MYTHREAD_H
#include <QTimer>
#include <QThread>
#include <iostream>
class MyThread : public QThread {
Q_OBJECT
public:
MyThread();
public slots:
void doIt();
protected:
void run();
};
#endif /* MYTHREAD_H */
MyThread.cpp
#include "MyThread.h"
using namespace std;
MyThread::MyThread() {
moveToThread(this);
}
void MyThread::run() {
QTimer* timer = new QTimer(this);
timer->setInterval(1);
timer->connect(timer, SIGNAL(timeout()), this, SLOT(doIt()));
timer->start();
}
void MyThread::doIt(){
cout << "it works";
}
As I commented (further information in the link) you are doing it wrong :
You are mixing the object holding thread data with another object (responsible of doIt()). They should be separated.
There is no need to subclass QThread in your case. Worse, you are overriding the run method without any consideration of what it was doing.
This portion of code should be enough
QThread* somethread = new QThread(this);
QTimer* timer = new QTimer(0); //parent must be null
timer->setInterval(1);
timer->moveToThread(somethread);
//connect what you want
somethread->start();
Now (Qt version >= 4.7) by default QThread starts a event loop in his run() method. In order to run inside a thread, you just need to move the object. Read the doc...
m_thread = new QThread(this);
QTimer* timer = new QTimer(0); // _not_ this!
timer->setInterval(1);
timer->moveToThread(m_thread);
// Use a direct connection to whoever does the work in order
// to make sure that doIt() is called from m_thread.
worker->connect(timer, SIGNAL(timeout()), SLOT(doIt()), Qt::DirectConnection);
// Make sure the timer gets started from m_thread.
timer->connect(m_thread, SIGNAL(started()), SLOT(start()));
m_thread->start();
A QTimer only works in a thread that has an event loop.
http://qt-project.org/doc/qt-4.8/QTimer.html
In multithreaded applications, you can use QTimer in any thread that has an event loop. To start an event loop from a non-GUI thread, use QThread::exec(). Qt uses the timer's thread affinity to determine which thread will emit the timeout() signal. Because of this, you must start and stop the timer in its thread; it is not possible to start a timer from another thread.
You can use the emit signal and start the timer inside the emitted slot function
main.cpp
#include "MyThread.h"
#include <iostream>
using namespace std;
int main(int argc, char *argv[]) {
MyThread t;
t.start();
while(1);
}
MyThread.h
#ifndef MYTHREAD_H
#define MYTHREAD_H
#include <QTimer>
#include <QThread>
#include <iostream>
class MyThread : public QThread {
Q_OBJECT
public:
MyThread();
QTimer *mTimer;
signals:
start_timer();
public slots:
void doIt();
void slot_timer_start();
protected:
void run();
};
#endif /* MYTHREAD_H */
MyThread.cpp
#include "MyThread.h"
using namespace std;
MyThread::MyThread() {
mTimer = new QTimer(this);
connect(this,SIGNAL(start_timer()),this, SLOT(slot_timer_start()));
connect(mTimer,SIGNAL(timeout()),this,SLOT(doIt()));
}
void MyThread::run() {
emit(start_timer());
exec();
}
void MyThread::doIt(){
cout << "it works";
}
void MyThread::slot_timer_start(){
mTimer->start(1000);
}
You need an event loop to have timers. Here's how I solved the same problem with my code:
MyThread::MyThread() {
}
void MyThread::run() {
QTimer* timer = new QTimer(this);
timer->setInterval(1);
timer->connect(timer, SIGNAL(timeout()), this, SLOT(doIt()));
timer->start();
/* Here: */
exec(); // Starts Qt event loop and stays there
// Which means you can't have a while(true) loop inside doIt()
// which instead will get called every 1 ms by your init code above.
}
void MyThread::doIt(){
cout << "it works";
}
Here's the relevant piece of the documentation that none of the other posters mentioned:
int QCoreApplication::exec()
Enters the main event loop and waits until exit() is called. Returns
the value that was set to exit() (which is 0 if exit() is called via
quit()). It is necessary to call this function to start event
handling. The main event loop receives events from the window system
and dispatches these to the application widgets. To make your
application perform idle processing (i.e. executing a special function
whenever there are no pending events), use a QTimer with 0 timeout.
More advanced idle processing schemes can be achieved using
processEvents().
I have created an example that calls the timer within a lambda function:
#include <QCoreApplication>
#include <QObject>
#include <QTimer>
#include <QThread>
#include <QDebug>
#include <memory>
int main(int argc, char** argv)
{
QCoreApplication app(argc, argv);
QThread* thread = new QThread(&app);
QObject::connect(thread, &QThread::started, [=]()
{
qInfo() << "Thread started";
QTimer* timer1 = new QTimer(thread);
timer1->setInterval(100);
QObject::connect(timer1, &QTimer::timeout, [=]()
{
qInfo() << "Timer1 " << QThread::currentThreadId();
});
timer1->start();
});
thread->start();
QTimer timer2(&app);
QObject::connect(&timer2, &QTimer::timeout, [=]()
{
qInfo() << "Timer2 " << QThread::currentThreadId();
});
timer2.setInterval(100);
timer2.start();
return app.exec();
}
Related
I use QTimer to send periodically 'Ping' packet to the server (MQTT client). But it timer is not absolutely accurate. After some time of working it has some delay and server broken connection.
I try to use different Qt::TimerType, but it does not help.
I need the most accurate timer. Do you have any ideas?
Thank you!
EDIT (Frederik solution)
I have done something this:
tthread.h
class TThread : public QThread
{
Q_OBJECT
void run();
public:
explicit TThread(QObject *parent = 0);
signals:
private slots:
void timerOut();
};
tthread.cpp
TThread::TThread(QObject *parent) : QThread(parent)
{
}
void TThread::run()
{
QTimer timer;
connect(&timer, SIGNAL(timeout()), this, SLOT(timerOut()), Qt::DirectConnection);
timer.start(1000);
timer.moveToThread(this);
exec();
}
void TThread::timerOut()
{
QTime time = QTime();
qDebug() << time.currentTime().toString();
}
main.cpp
TThread thread;
thread.start();
thread.setPriority(QThread::HighPriority);
Run a QTimer on a separate QThread which doesn't do anything else.
If you're running the timer on an already busy thread the timeout events may not come on time or not at all.
Make a worker class e.g. "PingPacketWorker", implement a slot that does pinging.
Make a QThread.
Connect your QTimer and PingPacketWorker signal/slots.
Start the timer.
Call moveToThread on the PingPacketWorker and the QTimer, the timer should restart because of moveToThread, note you can only start / stop it on the owner thread!
You could also increase your QThread's priority since you asked for "the most accurate" solution ...
Update:
Also, set QTimer::setTimerType(Qt::PreciseTimer)
The default Qt::CoarseTimer is less precise (5% of the interval)
#pragma once
#include <cstdint>
#include <QObject>
class PrecisePolling : public QObject
{
Q_OBJECT
private:
std::uint64_t previousPollingTime;
public:
PrecisePolling();
public slots:
void doPolling();
#include "precisepolling.h"
#include "QDateTime"
#include <QDebug>
PrecisePolling::PrecisePolling()
: previousPollingTime(QDateTime::currentMSecsSinceEpoch())
{}
void PrecisePolling::doPolling()
{
const std::uint64_t ms = QDateTime::currentMSecsSinceEpoch();
qDebug() << ms - previousPollingTime;
previousPollingTime = ms;
}
#include <QCoreApplication>
#include <QThread>
#include <QTimer>
#include "precisepolling.h"
int main(int argc, char *argv[])
{
QCoreApplication a(argc, argv);
QThread thread;
QTimer timer;
PrecisePolling pp;
timer.setInterval(1000);
timer.setTimerType(Qt::PreciseTimer);
QObject::connect(&timer, &QTimer::timeout, &pp, &PrecisePolling::doPolling);
timer.start();
timer.moveToThread(&thread);
pp.moveToThread(&thread);
thread.start(QThread::Priority::TimeCriticalPriority);
return a.exec();
}
Looking around I found mainly 3 proposals to sleep a QThread without blocking the event loop. But I don't know which one is the most efficient or if I should use one or the other depending of the circumstances.
Can someone explain me what is going on in each one? Is there any other proposal?
ClassB.h
#pragma once
#include <QtCore>
#include <QWaitCondition>
#include <QMutex>
#include <QEventLoop>
#include <QTimer>
#include <QDebug>
class ClassB : public QObject
{
Q_OBJECT
public:
ClassB()
{
qDebug() << "ClassB::ClassB";
_timer = new QTimer(this);
_timer->setInterval(100);
QObject::connect(_timer, &QTimer::timeout, [this]() { emit continousSignalToClassA(); });
_timer->start();
}
void start()
{
qDebug() << "ClassB::start";
while(true)
{
switch (3)
{
case 1:
{
QThread::msleep(200);
QCoreApplication::processEvents();
break;
}
case 2:
{
QEventLoop loop;
QTimer::singleShot(200, &loop, SLOT(quit()));
loop.exec();
break;
}
case 3:
{
QWaitCondition w;
QMutex mutex;
mutex.lock();
w.wait(&mutex, 200);
mutex.unlock();
QCoreApplication::processEvents();
break;
}
}
qDebug() << "waiting";
}
}
signals:
void continousSignalToClassA();
private:
QTimer* _timer;
};
ClassA.h
#pragma once
#include <QThread>
#include <ClassB.h>
class ClassA : public QObject
{
Q_OBJECT
public:
ClassA() {}
void launchClassBThread()
{
_classB = new ClassB();
QObject::connect(this, &ClassA::startClassB, _classB, &ClassB::start);
QObject::connect(_classB, &ClassB::continousSignalToClassA, this, &ClassA::signalReceived);
QThread *thread = new QThread();
_classB->moveToThread(thread);
thread->start();
emit startClassB();
}
void signalReceived()
{
qDebug() << "** I get a signal **";
}
signals:
void startClassB();
private:
ClassB *_classB;
};
Main.cpp
#include <QCoreApplication>
#include <ClassA.h>
int main(int argc, char *argv[])
{
QCoreApplication a(argc, argv);
ClassA classA;
classA.launchClassBThread();
return a.exec();
}
If you want to sleep a QThread use QThread::msleep(200); and accept the fact that events are blocked. Using QCoreApplication::processEvents(); is in most cases a bad idea and a design flaw.
If you want to execute a function every N seconds or milliseconds, use QTimer::singleShot and remove any self written loops. This is pretty much the same as QThread::sleep(N), it just doesn't block the event loop, unless you have expensive blocking code in the QTimer.
The run method of my QThread is finishing, but I cannot get the signal.
Here is the entire code:
My thread header:
#ifndef MYTHREAD_H
#define MYTHREAD_H
#include <QThread>
#include <QDebug>
#include "mydataobject.h"
class MyThread: public QThread
{
Q_OBJECT
public:
MyThread(MyDataObject data,
bool useData);
private:
void run();
signals:
void resultsReady(MyDataObject data);
private:
MyDataObject data;
bool useData;
};
#endif // MYTHREAD_H
My thread code:
#include "mythread.h"
MyThread::MyThread(MyDataObject data, bool useData)
{
this->data = data;
this->useData = useData;
}
void MyThread::run()
{
if( useData )
{
data.calculate(); // Do something
}
emit resultsReady(data);
qDebug() << "Thread finished";
}
My test header:
#ifndef THREADTESTER_H
#define THREADTESTER_H
#include <QDebug>
#include "mythread.h"
class ThreadTester: public QObject
{
Q_OBJECT
public:
ThreadTester();
void runTests();
public slots:
void threadFinished(MyDataObject data);
private:
MyDataObject data;
};
#endif // THREADTESTER_H
My test code:
#include "threadtester.h"
ThreadTester::ThreadTester(){}
void ThreadTester::runTests()
{
qRegisterMetaType<MyDataObject>("MyDataObject");
MyDataObject data;
MyThread* thread = new MyThread(data, true);
connect(thread, SIGNAL(resultsReady(MyDataObject)),
this, SLOT(threadFinished(MyDataObject)));
thread->start();
thread->wait();
}
void ThreadTester::threadFinished(MyDataObject data)
{
qDebug() << "TEST";
this->data = data;
}
Main function:
#include <QApplication>
#include "threadtester.h"
int main(int argc, char *argv[])
{
QApplication a(argc, argv);
ThreadTester threadTester;
threadTester.runTests();
return a.exec();
}
Why the public slot threadFinished is never called?
Note: the "Thread finished" message is appearing, but the "TEST" message not.
What happens in your code:
create QApplication
create ThreadTester
run method ThreadTester::runTests which does following:
creates tread object
connect to result
start thread
wait for thread…
now thread does it job and emits the signal
since you connect used default connection method, slot invocation is scheduled to be run in event loop which didn't start yet.
thread completes
…wait for thread completes (probably here you expecting final result, but look what happens later)
event loop is started
event loop executes queued invocation of slot ThreadTester::threadFinished
event loop waits for next events
Okay, so I've completely lost in QTimer. The problem is: I have multithreaded application, and I need to do some work on QTimer's timeout. I've done like this:
QTimer* timer = new QTimer();
timer->setSingleShot(true);
connect(timer, SIGNAL(timeout()), someObject, SLOT(work()));
And this did not work. Sometimes, work() was not called at all, sometimes it was called when I closed program, and sometimes all seemed normal.
So I've come to idea that timer needs thread. To provide MCV example :
class Tester : public QObject
{
Q_OBJECT
public:
Tester(QObject* par = 0) : QObject(par)
{
}
public slots:
void greet()
{
qDebug()<<"hello";
}
};
int main(int argc, char *argv[])
{
QCoreApplication a(argc, argv);
QTimer* timer1 = new QTimer();
QThread* thread = new QThread();
Tester* tester = new Tester();
timer1->setInterval(500);
timer1->setSingleShot(false);
timer1->moveToThread(thread);
QObject::connect(thread, SIGNAL(started()), timer1, SLOT(start()));
QObject::connect(timer1, SIGNAL(timeout()), tester, SLOT(greet()));
QObject::connect(timer1, SIGNAL(timeout()), timer1, SLOT(deleteLater()));
QObject::connect(timer1, SIGNAL(destroyed()), thread, SLOT(quit()));
thread->start();
thread->wait();
delete thread;
delete tester;
return a.exec();
}
And this example does nothing. It does not greet me, so timeout is not called, and it does not end, so thread is not stopped. So questions are:
1. What is wrong with this code?
2. How properly use QTimer in multithreaded environment?
Just calling QTimer::setInterval doesn't start the QTimer. It just sets the interval in which the QTimer::timeout signal is emitted. You didn't start the QTimer. Use QTimer::start.
I think you're doing several mistakes here. You need to move the timer to thread after all the connections are made. Not sure how safe would be to start the timer when the thread starts because at that point, the timer would be in one thread and the thread object would be in another thread. QTimer must be started from the same thread in which it was created. The QThread object is just a thread handler and it lives in the thread in which was created. See the modifications I made to your example:
#include <QCoreApplication>
#include <QThread>
#include <QDebug>
#include <QTimer>
#include <QObject>
//#include "tester.h"
class Tester : public QObject
{
Q_OBJECT
public:
Tester(QObject* par = 0) : QObject(par)
{
}
public slots:
void greet()
{
qDebug()<<"hello";
}
};
int main(int argc, char *argv[])
{
QCoreApplication a(argc, argv);
QTimer* timer1 = new QTimer();
QThread* thread = new QThread();
Tester* tester = new Tester();
timer1->setInterval(500);
timer1->setSingleShot(false);
timer1->start();
QObject::connect(timer1, SIGNAL(timeout()), tester, SLOT(greet()));
timer1->moveToThread(thread);
thread->start();
return a.exec();
}
It's not the safest/best fix(memory leaks and other) but it is, i hope, an example on which you can build.
This would be in my opinion the right and without memory leaks way to use a QTimer in another QThread:
handler.h
#ifndef HANDLER_H
#define HANDLER_H
#include <QObject>
class QTimer;
class QThread;
class Tester;
class Handler : public QObject
{
Q_OBJECT
public:
explicit Handler(QObject *parent = 0);
~Handler();
void exec();
private:
QTimer* timer;
QThread* thread;
Tester* tester;
};
#endif // HANDLER_H
handler.cpp
#include "handler.h"
#include "tester.h"
#include <QThread>
#include <QDebug>
#include <QTimer>
#include <QObject>
#include <QCoreApplication>
Handler::Handler(QObject *parent) :
QObject(parent)
{
timer = new QTimer;
thread = new QThread(this);
tester = new Tester(this);
timer->setInterval(500);
timer->setSingleShot(false);
QObject::connect(timer, SIGNAL(timeout()), tester, SLOT(greet()));
QObject::connect(thread, SIGNAL(destroyed()), timer, SLOT(deleteLater()));
}
Handler::~Handler()
{
thread->wait();
}
void Handler::exec()
{
timer->start();
timer->moveToThread(thread);
thread->start();
}
tester.h
#ifndef TESTER_H
#define TESTER_H
#include <QObject>
class Tester : public QObject
{
Q_OBJECT
public:
Tester(QObject* par = 0);
public slots:
void greet();
};
#endif // TESTER_H
tester.cpp
#include "tester.h"
#include <QDebug>
Tester::Tester(QObject *parent) :
QObject(parent)
{
}
void Tester::greet()
{
qDebug()<<"hello";
}
main.cpp
#include <QCoreApplication>
#include "handler.h"
int main(int argc, char *argv[])
{
QCoreApplication a(argc, argv);
Handler handler;
handler.exec();
return a.exec();
}
I have a class that should run in a thread and needs an event loop for the slots, currently I run it nicely with moveToThread(), but I'd like to use QThreadPool and I have encountered a problem.
When run with QThreadPool the run() method of my runnable is called from a pooled thread (I check this with QThread::currentThread()), but my slots aren't running in the pooled thread, so I think the object isn't moved to a thread in the pool.
I think this because I know the slots are run in the receiver's thread, which is exactly the (correct) behaviour I get when using the moveToThread() method and a QThread.
How do I get my QRunnable (Foo in the example below) to be run entirely in the pooled threads?
Or is it something I'm doing wrong or understood wrong?
The following POC demonstrates the problem:
foo.h
#ifndef FOO_H
#define FOO_H
#include <QObject>
#include <QRunnable>
#include <QEventLoop>
class Foo : public QObject, public QRunnable
{
Q_OBJECT
public:
explicit Foo(int data, QObject *parent = 0);
void run();
signals:
void startWorking();
public slots:
void doWork();
private:
QEventLoop eventLoop;
int data;
};
#endif // FOO_H
foo.cpp
#include "foo.h"
#include <QThread>
#include <QDebug>
Foo::Foo(int d, QObject *parent) :
QObject(parent), eventLoop(this), data(d)
{
}
void Foo::run()
{
qDebug() << "run() in: " << QThread::currentThread();
connect(this, SIGNAL(startWorking()), this, SLOT(doWork()));
emit startWorking();
eventLoop.exec();
}
void Foo::doWork()
{
qDebug() << "doWork() in: " << QThread::currentThread();
}
main.cpp
#include <QCoreApplication>
#include <QThreadPool>
#include "foo.h"
int main(int argc, char *argv[])
{
QCoreApplication a(argc, argv);
Foo *foo = new Foo(42);
QThreadPool::globalInstance()->start(foo);
return a.exec();
}
Please note, however, that in my real code the signal won't be emitted right away, because it will be after I receive some data on the network.
PS: The POC can also be found here.
Maybe you could split your logic in class Foo into two:
the hoster QRunnable with a QEventLoop, and a worker QObject, which you create on the worker thread in run() before calling QEventLoop::exec method. Then you forward all the signals to the worker object.
So now your slots will be called on the pooled thread.
However, QThreadPool is designed for executing lots of short tasks without creating too many simultaneous threads. Some tasks are enqueued and are waiting for others to finish. If this is not your intention, you might want to go back to good old QThread and use it instead.
You can support both modes but it will require some coordination from the outside. My strategy is to emit a signal from inside QRunnable::run passing the current thread. When you plan to use it in a thread pool, use a Qt::BlockingQueuedConnection on this signal and do your moveToThread there. Otherwise, move it to the QThread and emit a signal to start working as usual.
TaskRunner.h
#pragma once
#include <QObject>
#include <QRunnable>
#include <QThread>
class TaskRunner : public QObject, public QRunnable
{
Q_OBJECT
public:
TaskRunner(int data, QObject* parent = nullptr);
void run() override;
Q_SIGNALS:
void start();
void starting(QThread*);
void stop();
private:
int data;
};
TaskRunner.cpp
#include "TaskRunner.h"
#include <QEventLoop>
#include <stdexcept>
TaskRunner::TaskRunner(int data, QObject* parent)
: QObject(parent), data(data)
{
// start should call run in the associated thread
QObject::connect(this, &TaskRunner::start, this, &TaskRunner::run);
}
void TaskRunner::run()
{
// in a thread pool, give a chance to move us to the current thread
Q_EMIT starting(QThread::currentThread());
if (thread() != QThread::currentThread())
throw std::logic_error("Not associated with proper thread.");
QEventLoop loop;
QObject::connect(this, &TaskRunner::stop, &loop, &QEventLoop::quit);
// other logic here perhaps
loop.exec();
}
main.cpp
#include <QCoreApplication>
#include <QThreadPool>
#include "TaskRunner.h"
// comment to switch
#define USE_QTHREAD
int main(int argc, char *argv[])
{
QCoreApplication a(argc, argv);
auto runner = new TaskRunner(42);
#ifdef USE_QTHREAD
// option 1: on a QThread
auto thread = new QThread(&a);
runner->moveToThread(thread);
QObject::connect(thread, &QThread::finished, runner, &QObject::deleteLater);
Q_EMIT runner->start();
// stop condition not shown
#else
// option 2: in a thread pool
QObject::connect(
runner, &TaskRunner::starting,
runner, &QObject::moveToThread,
Qt::BlockingQueuedConnection);
QThreadPool::globalInstance()->start(runner);
// stop condition not shown
#endif
return a.exec();
}
Since the your connect call
connect(this, SIGNAL(startWorking()), this, SLOT(doWork()));
used the default parameter for connection type, it will be a Qt::Autoconnection.
The signal is emitted from the pooled thread, and the slot still belongs to foo, which has a thread affinity to the main thread. The autoconnection will decide to put the slot in the event queue of the main thread.
There are two ways you can fix this:
1.
connect(this, SIGNAL(startWorking()), this, SLOT(doWork()), Qt::DirectConnection);
and remove the eventloop.exec();
2.
in the run method, move the foo object to the current thread before connecting the signal and slot.