How to combine QtConcurrent with QMetaObject::invokeMethod?
-
Re: How to Use Non Static function in QtConcurrent::run() Method
Based on a previous post as well as the one referenced above, I have realized that I am trying to get a
QFuture
from runningQtConcurrent::run
on a method that accesses a GUI. I am doing this outside of whatever thread the GUI is on.// The callback here just sets a boolean so that a button can be enabled. [ connect(&m_modelsLoadedWatcher, &QFutureWatcher<bool>::finished, this, &ConfigDialog::onNNModelsLoaded); m_modelsLoadedFuture = QtConcurrent::run(engine, &wise::Engine::loadPytorchModelsAsync); m_modelsLoadedWatcher.setFuture(m_modelsLoadedFuture);
I read that
QMetaObject::invokeMethod
"invokes" the method you want from a child class of QObject.-
Does
invokeMethod
somehow cause the method to be called from the thread that is handling the GUI? Is this just to make sure that multiple threads aren't touching the same components? -
How does one put this together with
QtConcurrent::run
?
I need to get a
QFuture<void>
back, but passing in invokeMethod returns a bool. I'm unclear then how to mix the two since the return type of the invoked method is void to match my future.Should I just change the future's type from void to bool?
I apologize for the naivety. Here's what I tried:
I changed the above code to this:
m_modelsLoadedFuture = QtConcurrent::run( // loadModels touches GUI stuff, so have to use invokeMethod QMetaObject::invokeMethod(engine, // wrap in lambda, because you cant use a pointer for bound method //to do anything but call the method. [engine]() { engine->loadPytorchModelsAsync(); } ) );
However, this doesn't seem right to me. Now QtConcurrent is returning a future for the
invokeMethod
, which doesn't tell me anything about the final status ofloadPytorchModelsAsync
... For instancem_modelsLoadedFuture.isFinished()
would tell me only that the loading started, not that it was finished and returned. -
-
Does invokeMethod somehow cause the method to be called from the thread that is handling the GUI?
invokeMethod
has a connection type argument. By default it's auto-connection. This means that the method is called on the thread the target object (engine
in your code) lives in. If it lives on the GUI thread it's gonna be called on the GUI thread. If it lives on the same thread theinvokeMethod
was called on it will be called on the same thread, as if by a direct function call. If the method thatinvokeMethod
runs is executed on a different thread then by default it is fire-and-forget type of call i.e. it queues the call on the target thread and returns immediately. If you want to wait for it to finish then you need to tell it to block via the connection type argument.I need to get a QFuture<void> back, but passing in invokeMethod returns a bool
The future should have the same type as the return type of the function you're calling, because it is used to get that return value. So if your
loadPytorchModelsAsync
returns a bool the future will be of typeQFuture<bool>
and the same for the watcherQFutureWatcher<bool>
. IfloadPytorchModelsAsync
is void then the future and the watcher should be too.How does one put this together with QtConcurrent::run?
So, although syntactically correct what you wrote makes little sense.
In your codeQtConcurrent::run
startsQMetaObject::invokeMethod
on another thread. Because you didn't specify otherwise,QMetaObject::invokeMethod
uses an auto connection, so adds an event to theengine
thread and that calls the lambda. So it's pretty much as if you'd call the invokeMethod directly, without QtConcurrent, just slower because it delegates that to another thread.If I understand you correctly you want to run some code asynchronously, get a future watcher for it and then do something when it finishes. That code needs to call something on the GUI thread as part of whatever it is doing.
If that's what you mean then you'd rather have something like this:// This will run the lambda that calls loadPytorchModelsAsync on another thread m_modelsLoadedFuture = QtConcurrent::run([=](){ return engine->loadPytorchModelsAsync(); }); // and then setup the watcher like you did
and your
loadPytorchModelsAsync
method would look something like this:bool wise::Engine::loadPytorchModelsAsync() { // do some stuff on this thread // run DoSomethingOnTheHomeThread on the thread this instance lives in (GUI?) // and block until it finishes QMetaObject::invokeMethod(this, &wise::Engine::DoSomethingOnTheHomeThread, Qt::BlockingQueuedConnection); // do some other stuff on this thread return something; }