Qt terminate thread spawn by QConcurrent::run

2019-05-28 06:44发布

问题:

Platform: Win7 x64, MinGW-rubenvb (4.7.2-x64), Qt 4.8

Say I have few lengthy tasks (read population file, write population file, and run simulation) spawned using QConcurrent::run, as follow:

void MainWindow::runLengthyJob() {
    /* some setup */
    jobWatcher->setFuture(QConcurrent::run(theApp, &AppClass::lengthyJob));
    // lengthyJob - can be readPop(), writePop(), runSim()
}

Usually those tasks take at least 15 seconds to finish (for simulation, it takes more than hours). To prevent crashing from not-yet-finished-threads, I re-implement the MainWindow::closeEvent as follow:

void MainWindow::closeEvent(QCloseEvent *event) {
    /* wait for any dangling thread */
    if (QThreadPool::globalInstance()->activeThreadCount())
        QThreadPool::globalInstance()->waitForDone();
    event->accept();
} // end: MainWindow::closeEvent

This works okay but when I click the 'x' button of MainWindow, it seems freezing and showing 'Not responding' (I guess the text is given by OS), although it finally terminated the app.

I want to quit the app as soon as the close() slot of MainWindow is triggered. So, how can I shorten the waiting time for not-yet-finished-threads? Or how can I notify user that there is still a lengthy job running (which can takes hours before completely shut down)? (I tried to include QDialog/QMessagebox in closeEvent but the newly created widgets also freeze)

Note: For AppClass:lengthyJobs [readPop() /writePop()], those are self-contained functions and I cannot break downs to smaller steps. For AppClass::runSim(), smaller steps may be possible.

回答1:

First of all, the future, returned by QtConcurrent::run doesn't support cancelling. So you can't just cancel it. The best you can do is to set some flag in the AppClass and check it in your lengthyJobs (as you said it is possible in the case of runSim), then wait for completion.

Either way, if you want to notify user of running task, you can do the following: make a subclass of QDialog, say WaitDialog with QTimer with some period, start this timer at dialog construction, connect timeout() signal to some slot in this dialog, and accept the dialog if there's no running thread:

if (QThreadPool::globalInstance()->activeThreadCount() == 0)
    accept();

and then in MainWindow::closeEvent:

void MainWindow::closeEvent(QCloseEvent *event) {
    WaitDialog dlg;
    dlg.exec();
    event->accept();
}