Is using QTimer:: singlehot a good way to modify objects in other threads?
-
I don't see any overloads for
QTimer
to useQeuedConnection
type. And in any case, you're using a lambda so it will be called in the same thread. So, the answer seems to be "no". -
For example, if I want to modify the text of the label in the background thread, I can use QTimer:: singlehot (0, label, [=] () {label ->setText ("abc");}) in program;
-
I don't see any overloads for
QTimer
to useQeuedConnection
type. And in any case, you're using a lambda so it will be called in the same thread. So, the answer seems to be "no".@sierdzio said in Is using QTimer:: singlehot a good way to modify objects in other threads?:
I don't see any overloads for
QTimer
to useQeuedConnection
type. And in any case, you're using a lambda so it will be called in the same thread. So, the answer seems to be "no".The "context" object (which @John-Van provided) determines which thread the lambda will run in. It also activates
Qt::QueuedConnection
if the timer and the context object live in different threads. So, @John-Van's code will work fine.Having said that,
QMetaObject::invokeMethod()
is a bit more intuitive thanQTimer::singleShot()
: https://doc.qt.io/qt-6/qmetaobject.html#invokeMethod-8 -
For example, if I want to modify the text of the label in the background thread, I can use QTimer:: singlehot (0, label, [=] () {label ->setText ("abc");}) in program;
@John-Van
Although all the answers are excellent, especially @JKSH's explanation, I don't understand how your question and the code you show relate to each other.QTimer::singleShot()
does not run in the background and does not involve another thread. Furthermore, and of particular note, are you aware that in Qt you must not access any UI element for either read or write in any other thread than the main UI one, so you cannot "modify the text of a label in a background/other thread" in any case? Unless you mean that it is yourQTimer::singleshot()
which itself is invoked from another thread while thelabel
context is in the main thread, but I didn't get that from your phrasing.
[UPDATE}
And now @VRonin has expanded upon that by questioning how such a secondary thread would safely gain access tolabel
anyway. -
To expand on JonB's post, I assume
QTimer::singlehot(0, label, [=](){label->setText ("abc");})
is called from a secondary thread. Thatlabel
pointer should never have made it to the secondary thread. It's a race condition unless it's atomic (e.g.QAtomicPointer
). -
Oh hello there, dear bot.
-
@John-Van
Although all the answers are excellent, especially @JKSH's explanation, I don't understand how your question and the code you show relate to each other.QTimer::singleShot()
does not run in the background and does not involve another thread. Furthermore, and of particular note, are you aware that in Qt you must not access any UI element for either read or write in any other thread than the main UI one, so you cannot "modify the text of a label in a background/other thread" in any case? Unless you mean that it is yourQTimer::singleshot()
which itself is invoked from another thread while thelabel
context is in the main thread, but I didn't get that from your phrasing.
[UPDATE}
And now @VRonin has expanded upon that by questioning how such a secondary thread would safely gain access tolabel
anyway.@JonB
I use a demo to refine the details.#include<Qtimer> #include<QLabel> #include<QThread> #include<QApplication> int main(int argc, char* argv[]) { QApplication app(argc, argv); auto lab = new QLabel("123"); lab->show(); //I ensure that the object to which the label pointer refers is not deleted after its creation, and the label pointer does not point to any other objects. QThread::create([=]() { //Assuming that the text of the label requires a lengthy calculation in the secondary thread, I simulate this by using sleep for one second in a simple thread QThread::sleep(1); //Then I want to directly set the value of the label in a non GUI thread QTimer::singleShot(0, lab, [=]() { lab->setText("456"); }); })->start(); return app.exec(); }
Is this an easier implementation than using Qt’s signals and slots?
-
@JonB
I use a demo to refine the details.#include<Qtimer> #include<QLabel> #include<QThread> #include<QApplication> int main(int argc, char* argv[]) { QApplication app(argc, argv); auto lab = new QLabel("123"); lab->show(); //I ensure that the object to which the label pointer refers is not deleted after its creation, and the label pointer does not point to any other objects. QThread::create([=]() { //Assuming that the text of the label requires a lengthy calculation in the secondary thread, I simulate this by using sleep for one second in a simple thread QThread::sleep(1); //Then I want to directly set the value of the label in a non GUI thread QTimer::singleShot(0, lab, [=]() { lab->setText("456"); }); })->start(); return app.exec(); }
Is this an easier implementation than using Qt’s signals and slots?
-
@John-Van You're updating the label in the second thread - this is not supported!
Use QMetaObject::invokeMethod() if you don't want to use signals/slots.@jsulm said in Is using QTimer:: singlehot a good way to modify objects in other threads?:
You're updating the label in the second thread - this is not supported!
Actually, now he has a receiver object (
lab
) which I would expect to run the lambda in the correct thread.Still, I agree that using QMetaObject::invokeMethod makes a lot more sense. Just for completeness' sake, here is how I use it:
QMetaObject::invokeMethod(qApp, [=]() { lab->setText("456"); });
qApp
works really well as a context object in this case. It is short to type and you don't have to figure out a good context object to place here. It is so common in our program (which is quite old, so I don't want to introduce signals everywhere), that I wrote a shorter function nameguiThread(...)
which just takes the lambda. You can get it from https://github.com/SimonSchroeder/QtThreadHelper. It also makes the intent much clearer that you are trying to run code inside the GUI thread.