Offloading a cancelable job to a different thread that needs to update UI



  • I want to offload a function to a different thread because it takes a long time to execute and blocks the main thread (and freezes the UI). Here is a simplified version of the code:

    // This function is called when a button is pressed, and it can
    // be called as many times as the user wants. By convention, calling
    // this function while a job is already running will cancel that
    // job and start a new one.
    void Data::RefreshData() {
        if (future.isRunning()) {
            cancelDataGeneration();
            future.waitForFinished();
        }
    
        future = QtConcurrent::run(this, &Data::RegenerateData);
    }
    

    The issue I have is that a part of the work of Data::RegenerateData needs to be ran in the main thread (UI updates and other things). I put the code that need to be executed in the main thread in a different function and the end of Data::RegenerateData emits a signal that triggers this function. However, I now have a different issue: the return of future.waitForFinished() no longer means that the complete job is finished.

    Instead of waiting for future.waitForFinished() to finish, I would like to also wait for the work done in the main thread. I initially tried to achieve this with a QEventLoop that waits for a signal.

    void Data::RefreshData() {
        if (future.isRunning()) {
            cancelDataGeneration();
            future.waitForFinished();
        }
    
        future = QtConcurrent::run(this, &Data::RegenerateData);
    }
    
    // Runs in a worker thread
    void Data::RegenerateData() {
        // Long-running task
    
        // Waiting for the work on the main thread to finish... 
        QEventLoop loop;
        connect(this, SIGNAL(UpdateFinished()), &loop, SLOT(quit()));
        emit Update(); // connected to the OnUpdate slot.
        loop.exec();
    }
    
    void Data::OnUpdate() {
        // Update stuff
    
        emit UpdateFinished();
    }
    

    The problem with this approach is that it will always result in a deadlock. What is wrong with my approach, and how can I achieve what I need?


  • Qt Champions 2018

    Use a QFutureWatcher to get noticed when the future is done and do the rest of the work in the slot connected to QFutureWatcher::finished()



  • @christian-ehrlicher I don't understand what would be the difference between executing the rest of the work from QFutureWatcher::finished() instead of emitting it from the concurrent task.


  • Qt Champions 2018

    Since there is only one main thread I don't understand your statement here:

    I would like to also wait for the work done in the main thread

    From which thread do you call QtConcurrent::run()? Another one than the main thread?


 

Looks like your connection to Qt Forum was lost, please wait while we try to reconnect.