I updated the UI in the sub thread, but the program did not crash
-
wrote on 8 Dec 2023, 06:23 last edited by
Hello everyone,
I created a timer, move it to a subthread,and update button text when timeout.but the program not crash. Why?class A :public QWidget { public: A() { auto btn = new QPushButton(this); auto timer = new QTimer(); auto thread = new QThread(); timer->moveToThread(thread); thread->start(); connect(timer, &QTimer::timeout, [=]() { btn->setText("11111"); }); connect(btn, &QPushButton::clicked, timer, [=]() { timer->start(1000); }); } }; int main(int argc, char *argv[]) { QApplication app(argc, argv); A a; a.show(); return app.exec(); }
-
Hello everyone,
I created a timer, move it to a subthread,and update button text when timeout.but the program not crash. Why?class A :public QWidget { public: A() { auto btn = new QPushButton(this); auto timer = new QTimer(); auto thread = new QThread(); timer->moveToThread(thread); thread->start(); connect(timer, &QTimer::timeout, [=]() { btn->setText("11111"); }); connect(btn, &QPushButton::clicked, timer, [=]() { timer->start(1000); }); } }; int main(int argc, char *argv[]) { QApplication app(argc, argv); A a; a.show(); return app.exec(); }
@John-Van
A lucky day, it seems. Maybe consider purchasing a lottery ticket :-) -
@John-Van
A lucky day, it seems. Maybe consider purchasing a lottery ticket :-)wrote on 8 Dec 2023, 07:18 last edited by@Axel-Spoerl That is to say, whether one crash or not depends on luck?
-
@Axel-Spoerl That is to say, whether one crash or not depends on luck?
@John-Van It depends on many factors when using threads. Just because this code did not yet lead to any crashes does not mean it is OK to access UI from non-UI threads.
-
@John-Van It depends on many factors when using threads. Just because this code did not yet lead to any crashes does not mean it is OK to access UI from non-UI threads.
@John-Van
Most of the crashes I saw in this context were concurrent accesses to non-atomic members. I remember a UI randomly crashing, because a worker thread updated a progress bar in the UI. Doesn't seem so far-fetched. When the UI wanted to paint the progress bar, it accessed the progress data member. That worked nicely, unless the UI thread had another update at the same time => booom.The more complex a UI gets, the busier is its event loop. That increases the likelihood of data races and crashes. But as @jsulm said, a lot of factors are in play and most of them (e.g. mouse events) are unpredictable. So it's fair to call your day a lucky one :-)
-
-
@Axel-Spoerl That is to say, whether one crash or not depends on luck?
wrote on 8 Dec 2023, 22:01 last edited by@John-Van Nothing about it being unsupported guarantees a crash. It's just unsupported with undefined behavior. So if you have a race condition that breaks something, anything can happen. It could crash. Or it could have a subtle issue where data gets overwritten and passwords get silently sent down a network socket. And whatever it appears to be doing today could change tomorrow, or in a moment. Race conditions with multithreading can literally behave completely different if the temperate changes outside, and the CPU clock goes up/down slightly because of the temperature change.
That it doesn't crash isn't particularly interesting. It's still incorrect.
-
wrote on 11 Dec 2023, 08:04 last edited by
@John-Van said in I updated the UI in the sub thread, but the program did not crash:
That is to say, whether one crash or not depends on luck?
Yes, it does. From my experience I would say that having luck in this context is mostly deterministic: As long as you don't change your code you could continue to be lucky (and I would claim that the odds are slightly stacked towards not crashing). However, any change in your code (most likely totally unrelated) will lead to crashes eventually.
BTW, in your case this is really easy to fix. Just write:
connect(timer, &QTimer::timeout, this, [=]() { btn->setText("11111"); });
The third argument (if present) is a context object. It has a double purpose: 1) If the context object is deleted, the connection will be disconnect (since you rely on capturing
this
this would be a good idea). 2) More importantly in your case, the lambda will be executed in the thread of the context object (if I'm not mistaken).
1/7