Unsolved How to update QWidget content from a background thread lambda.
-
Hi
I have a class MyWorkerClass which use a separate thread to perform calculations. I can specify lambda callback to be notified on updates like this:
MyWorkerClass::instance().setCallback( | |() { printf("completed"); });
I also have a QWidget subclass instance which displays data.
I want to update the content of my widget from the lambda expression above.Looks like I can do this that simple:
MyWorkerClass::instance().setCallback( | this | () { onUpdate(); });
But if user decide to close the window with my widget - the lambda will try to call method of already deleted class instance.
Of course I can zero the lambda in the destructor of my QWidget subclass. But this will result in race conditions.What is the right way to update my QWidget from the lambda ?
-
This is wrong on so many levels.
Firstly, you're introducing a global state for no reason - yes, singletons are pretty stupid and useless.
Secondly, you're assuming that you can call whatever you like from one thread to the next. There's such a thing called race condition, you can't just call functions around. A thread is a function, that function has a call stack, calling something from one call stack into the next one unless specific protection measures are taken (i.e. guaranteeing thread safety) is a no-no.
Thirdly, you shall not touch anything GUI from a thread different than the GUI one (a consequence of 2). Moreover, GUI calls can't be made thread-safe, and are not reentrant. Meaning not only can't you call methods into the GUI, but you also can't create GUI objects into a separate thread.
Finally, the solution is: to have a worker object, which is moved in the proper thread by means of
QObject::moveToThread
. This worker object is then to fire a signal, which is connected to the relevant GUI objects' slots and updates the GUI elements by queuing an event over the main thread's event loop (which is the default behaviour). -
"Secondly, you're assuming that you can call whatever you like from one thread to the next..."
Thank you very much for the detailed explanation! I will take it into account.@kshegunov said in How to update QWidget content from a background thread lambda.:
Finally, the solution is: to have a worker object,
You mean I should rewrite a third-party code with QThread and QObject::moveToThread ?
Is there any solutions without rewriting 3rd party code ? -
@kshegunov said in How to update QWidget content from a background thread lambda.:
updates the GUI elements by queuing an event over the main thread's event loop (which is the default behaviour).
Is it safe to post an event from my lambda like this:
MyWorkerClass::instance().setCallback( | | () {
QCoreApplication::postEvent(NULL /* ptr to the receiver */, new MyEvent);
});and then handle the event in my widget class ? Or the receiver must be not null and I have to specify pointer to the QWidget subclass instance? (The documentation does not say anything about the receiver parameter can be null or not)
If I have to specify the pointer - It can become a dangling pointer. How to avoid this? -
@mikhail_kr said in How to update QWidget content from a background thread lambda.:
Is it safe to post an event from my lambda like this
It is safe, but you need to pass the pointer to the object which is going to receive the event.
nullptr
isn't acceptable.If I have to specify the pointer - It can become a dangling pointer. How to avoid this?
Generally not an easy thing to do. You can try
QPointer
, but I'm not 100% if it's acceptable across threads.