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