c++multithreadingqtqeventloopqfuture

How do you wait for `QtConcurrent::run` to finish without thread blocking?


Hey this should be a pretty straightforward question. Simply put:

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?


Solution

  • 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.