QThread infinite loop
-
Hello everyone,
How do I perform an infinity function with an interval?
When I inherit QThread and override run() I call exec() inside it, but it blocks everything that comes after.MyClass::MyClass(): i(0) { qDebug() << " MyClass() " << QThread::currentThread(); moveToThread(this); start(); } void MyClass::run(){ qDebug() << "MyClass::run() " << QThread::currentThread(); qDebug() << "before exec()"; exec(); for (int var = 0; var < 5; ++var) { emit emitMyClass("MyClass" + QString::number(i++)); qDebug() << " Emit! " <<QThread::currentThread(); QThread::sleep(5); } qDebug() << "after exec()"; }
thanks in advance,
Sal -
When I inherit QThread and override run() I call exec() inside it, but it blocks everything that comes after.
Exactly as documented.
You do not need a thread to do periodic execution of some action.
QTimer *timer = new QTimer(this); connect(timer, &QTimer::timeout(), someQObject, &SomeQObjectClass::timeoutSlot()); timer->setInterval(5); // milliseconds timer->start();
Whatever happens inside the slot needs to be short running. There is no loop in the slot. This object should maintain a run count (to replace
var
) and either stop the timer when 5 is reached, or reset the count to 0 etc. -
@salvatore-proxy
Thanks @c-cardona ,
the example above of the for going from 0 to 5 was an example , also imagine it as awhile(1){ doSOmethings }
I need to periodically perform a function but in a separate thread, ie,
I have the main thread doing things, I need to create a new thread where within it I perform a function periodically, I have succeeded by overriding the run() methodvoid run(){ init(); while(true) { periodicFunction(); QThread::sleep(2); } }
this way it works but I don't have the event loop active via exec();
void run(){ init(); exec(); // Start event loop while(true) { periodicFunction(); QThread::sleep(2); } }
exec() after this instruction nothing is executed, only after calling exit() does the flow of the run() function resume;
-
@salvatore-proxy
You can use the event loop inexec()
plus aQTimer
to do the same thing as yourwhile(true) sleep();
. You would not want to use both of these, either-or. -
@JonB
something likevoid run(){ init(); timer = new QTimer(); timer->setInterval(1000); QObject::connect(timer, &QTimer::timeout, this, &MyClass::periodicFunction); timer->start(); exec(); // Start event loop }
I used the run() method by also calling exec() so I have event loops to use signals and slots, but I have an internal timer that calls my run periodically, do you agree?
-
@salvatore-proxy
The code looks fine. I'm afraid I don't understand what the question is about an internal timer. -
@JonB
My question is it right to use a QTimer within a Qthread?
My needs are to:- launch a new thread that can handle slots and signals ( event loop is needed for this)
- run in the new thread the slots connected to signals from other threads and periodically run a task that generates a signals to other threads
-
@salvatore-proxy
Yes usingQTimer
inQThread
(withexec()
running) is fine and sounds like what you want for a periodic task. -
exec()
starts an event loop just like you already do when callingapp.exec()
insidemain()
. This is the reason why it blocks.Overriding
QThread::run()
is the Java way to do multi-threading. Qt started moving away from this (and even the documentation does not teach this approach as the normal one anymore). The default implementation ofrun()
will just callexec()
. Instead, Qt promotes a worker object approach. Inherit from QObject and implement a slot (or just use a lambda if that is sufficient). The QTimer object would also then be created from another thread and then moved into the thread usingQObject::moveToThread()
.You source should then look something like this:
class MyClass : public QObject { Q_OBJECT public slots: void periodicFunction() {...} }; //-----8<--------------------------------------------- QThread *thread = new QThread(); thread->start(); // could also be done later MyClass *obj = new MyClass(); obj->moveToThread(thread); QTimer *timer = new QTimer(); timer->setInterval(1000); QObject::connect(timer, &QTimer::timout, obj, &MyClass::periodicFunction); timer->moveToThread(thread); timer->start();
If I'm not mistaken, even if you don't move the timer into the thread (but do move the instance of MyClass) it will work correctly. The timeout would then fire inside the main thread, but signal/slot connection will work across thread boundaries. And because
obj
is located inside the thread (because ofmoveToThread()
) the slot will be executed with the separate thread.If you just want a simple function, you could also connect to a lambda instead:
QObject::connect(timer, &QTimer::timeout, [](){ ...});
However, the lambda is not located in any specific thread. This would mean you have to move the timer again into the thread (whereas before this was optional).
You could, however, provide a context object for the lambda. Maybe this would start execution of the lambda inside the corresponding thread even if the time lives inside the main thread:
QObject::connect(timer, &QTimer::timeout, thread, [](){ ... });
We heavily rely on plain QThreads (without inheritance) in our software. Usually, we don't need repeated calls. An easy start is
QThread::create([]() {...})->start();
. But, you need to make sure to delete the thread itself at the end. For a worker thread you can useQMetaObject::invokeMethod(thread, []() {...});
for a one-time slot to be handled by a running QThread. -
@SimonSchroeder said in QThread infinite loop:
If I'm not mistaken, even if you don't move the timer into the thread (but do move the instance of MyClass) it will work correctly. The timeout would then fire inside the main thread, but signal/slot connection will work across thread boundaries
Question: Suppose the main thread is (perhaps incorrectly) "blocked" (or busy doing some computation). In that case although the signal is connected "queued across thread" would the signal not be acted on/the slot get queued for execution in the thread at the instant of the
emit
, would it have to wait till the next time the main thread's event loop is hit (whenever that might be)? -
@JonB said in QThread infinite loop:
would it have to wait till the next time the main thread's event loop is hit
From my understanding: yes.
-
@SimonSchroeder said in QThread infinite loop:
//-----8<--------------------------------------------- QThread *thread = new QThread(); thread->start(); // could also be done later MyClass *obj = new MyClass(); obj->moveToThread(thread); QTimer *timer = new QTimer(); timer->setInterval(1000); QObject::connect(timer, &QTimer::timout, obj, &MyClass::periodicFunction); timer->moveToThread(thread); timer->start();
Multithreading is hard! The timer->start() should come before timer->moveToThread(). I would also establish the connection from the QTimer instance to MyClass instance before moving the later. It's paranoia in this case, but prevents connecting to deleted objects in some realistic scenarios.