c++multithreadingqtqtconcurrent

QtConcurrent wait for finished when App is about to quit


I can't find any clear solution to this. I have a thread started with QtConcurrent::run(). When I close the application before the thread is finished, the application crashes. I want the application to close after all backgroud threads (QtConcurrent::run()) have finished. How do I solve this?


Solution

  • I came here looking for the same thing as you and ended up solving it in my own way. This is my approach:

    // [...] All necessary includes would go here
    
    int main(int argc, char *argv[])
    {
        // Keep track of time
        quint64 start=QDateTime::currentMSecsSinceEpoch();
    
        // Qt app instance
        QCoreApplication app(argc, argv);
    
        // Someplace safe to keep our futures
        QList<QFuture<void> > futures;
    
        //Prepare the lambda that does the heavy lifting
        auto lambda = [&] (void) {
            // [...] Heavy lifting goes here
        };
    
        // Run up some processing
        for(/* any number of heavy liftings we need */){
            // Keep the returned future
            auto future =  QtConcurrent::run(lambda, /* parameters would go here*/ );
            // Store the future around for later
            futures.append(future);
        };
    
        //Now all the heavy lifting has started, and we are ready to wait for them all to complete.
        for(auto future:futures){
            // Note that if the future finished BEFORE we call this, it will still work.
            future.waitForFinished();
        }
    
        // Spit out the number of seconds we were running
        quint64 end=QDateTime::currentMSecsSinceEpoch();
        qDebug()<<"DONE after" <<(((qreal)end-start)/1000.0)<<" sec";
    
        //NOTICE: I did not need an event loop so no app.exec() call here
    }
    

    UPDATE 2018

    I have since I wrote this answer gotten wiser, and decided to share another way to do that in some cases will be more elegant and save you some typing/boilerplate. It is called map-reduce and the nice bit is you don't need all of it, just the map part will work fine.

    NOTE: It is a quick adaption of this example in the official documentation. See this example if you want to retain some output data as well.

    // [...] All necessary includes would go here
    
    int main(int argc, char *argv[])
    {
        QGuiApplication app(argc, argv);
    
    
        // Create a list containing data to be processed
        QList<MyClass> tasks;
    
        // At this point fill the list of tasks with whatever you need
    
        // This is the actual code that will run per task
        std::function<void(const MyClass&)> myOperation = [](const MyClass &task)
        {
            qDebug() << "Doing myOperation() in thread" << QThread::currentThread();
            // Do heavy lifting on task object here
        };
    
        // Start the processing. QConcurrent will automagically start up threads, distribute the tasks and run them taking care of all the tedious threads management for you.
        // It can also take care of collecting the output (not shown in this example).
        // Finally , and most importantly the answer to the question; using the blockingMap will actually block execution until all the work is done.
        QtConcurrent::blockingMap(tasks, myOperation);
    
        return 0;
    }