Hey this should be a pretty straightforward question. Simply put:
- Want to run a function in another thread.
- Need to wait for the function to finish.
- Do not want to freeze the thread though while waiting.
- In other words, I'd like to use an eventloop.
Here is the freezing example:
extern void sneed()
{
QEventLoop wait;
wait.exec();
}
int main( int argc, char *argv[] )
{
QApplication a(argc, argv);
{
// this starts a tui
QConsoleToolkit::s_CursesController.start( QCD::CursesEngine::Engine_Thread_Stdout_Monitor );
}
ct_Start( "Sneed" );
QFuture<void> ff = QtConcurrent::run(sneed);
ff.waitForFinished(); // This freezes the tui
ct_Finish( "Chuck" );
}
I tried to use a QEventLoop in the main thread instead of ff.waitForFinished()
, but I could not figure out how I could emit a signal when ff
was finished, because QFuture isnt a QObject, and has no finished
signal that I could bind to:
https://doc.qt.io/qt-6/qfuture.html
I tried passing a QObject via reference to emit a signal from it instead, but couldnt get it to compile.
What am I missing here?
CodePudding user response:
The solution comes from a simple class called QFutureWatcher:
doc.qt.io/qt-5/qfuturewatcher.html
Here is some sample code for running lambda's in a different thread, and receiving its value.
template <class T>
auto asynchronous( auto &&lambda )
{
QEventLoop wait;
QFutureWatcher<T> fw;
fw.setFuture( QtConcurrent::run(lambda) );
QObject::connect ( &fw, &QFutureWatcher<T>::finished, &wait, &QEventLoop::quit );
wait.exec();
QObject::disconnect( &fw, &QFutureWatcher<T>::finished, &wait, &QEventLoop::quit );
return fw.result();
}
using the function would look like this:
int n(0);
ct_Start(n); // 0 - Running in main thread
n = asynchronous<int>([&n](){
// Running in background thread.
// Mainthread is waiting for this lambda to finish
// but mainthread is not locked.
// User Interface will still function.
for ( int i = 0; i < 100000; i ){
ct_Debug(n );
};
return n;
});
ct_Finish(n); // 100000
Note: ct_Debug ct_Start ct_Finish
are not found in the Qt framework. They are debugging macros for a TUI.
CodePudding user response:
I think you misunderstood my comment. This really is just a follow-up comment. It's not a real answer because I'm not a Qt expert, but I posted it as an "answer" so that I could include a code example.
The question that I tried to ask in my earlier comment was, why can't you do this?
extern void sneed() {
...
}
void sneed_wrapper() {
sneed();
...emit a signal...
}
int main( int argc, char *argv[] )
{
...
ct_Start( "Sneed" );
QFuture<void> ff = QtConcurrent::run(sneed_wrapper);
...
}
You said that you could solve your problem if the thread that runs sneed()
would send a signal that would alert the main event loop when the thread ends.
OK, so why not have the thread run sneed_wrapper()
as shown above? sneed_wrapper()
runs sneed()
and then when sneed()
is finished, it can do whatever it takes in Qt* to send the signal that will alert the main loop.
* Did I mention that I don't know Qt?