Qt 对话框关闭时如何终止异步函数

How to terminate async function when the Qt dialog is closed

背景

我有一个对话框,它在初始化时运行一个耗时的操作。为了不冻结 GUI,我将此操作包装到一个异步函数中。

例子

想象一个 dialog/widget 显示从远程服务器异步获取的当前天气:

Dialog::Dialog()
{
    auto label = new QLabel(this);
    QtConcurrent::run([=]() {
        const int temperature = getWeather(); // Time-consuming function
        label->setText(temperature);
    });
    // The rest code, layouts initialization, etc.
}

问题

如果这个dialog/widget在异步操作完成之前关闭,label->setText()部分显然会导致崩溃,因为那个时候widget对象将不存在。

问题

处理这种情况的正确方法是什么?可能,我应该使用其他东西而不是 QtConcurrent(例如 QThread),以便在对话框关闭时正确取消异步功能。

备注

请注意,实际代码是关于读取一堆文件,不是关于网络,这就是为什么使用异步 QNetworkRequest 接口不是案例。

QtConcurrent::run() 会 return 你 QFuture<T>。您应该可以在上面调用 QFuture::waitForFinished

另一件事是,您不应被允许从另一个线程调用 QLabel::setText,而是使用 QMetaObject::invokeMethod 或发出信号。

// global or class member
QFutureWatcher<int> g_watcher;

Dialog::Dialog()
{
    connect(&g_watcher, &QFutureWatcher<int>::finished, this, &Dialog::handleFinished);

    QFuture<int> future = QtConcurrent::run([]() -> int
    {
        int temperature = getWeather(); // Time-consuming function

        return temperature;
    });

    g_watcher.setFuture(future);
}

void Dialog::handleFinished()
{
    // will never crash because will not be called when Dialog destroyed
    ui->label->setText(QString::number(g_watcher.result()));
}

也可以断开连接到完成信号的所有东西:

disconnect(&g_watcher, &QFutureWatcher<int>::finished, 0, 0);

p.s。至于取消异步操作,用QtConcurrentQThread方法无法正确取消。

QThread::terminate() 方法,但来自文档:

...Warning: This function is dangerous and its use is discouraged. The thread can be terminated at any point in its code path...

因此你必须在你的 getWeather() 函数中实现一些 "cancel" 标志,或者按照上面写的那样做。