Qt对话框关闭时如何终止异步功能

时间:2019-12-02 13:37:16

标签: qt asynchronous qwidget qthread qtconcurrent

背景

我有一个对话框,该对话框在初始化时会执行耗时的操作。我将此操作包装到一个异步函数中,以免冻结GUI。

示例

想象一个对话框/窗口小部件,它显示了从远程服务器异步获取的当前天气:

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

问题

如果在异步操作完成之前关闭此对话框/窗口小部件,则label->setText()部分显然会导致崩溃,因为那一刻该窗口小部件对象将不存在。

问题

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

注意

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

2 个答案:

答案 0 :(得分:4)

// 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()方法,但来自文档:

  

...警告:此功能很危险,不建议使用。的   线程可以在其代码路径中的任何位置终止...

因此,您必须在getWeather()函数内部实现一些“取消”标志,或者按照上面的说明进行操作。

答案 1 :(得分:0)

QtConcurrent::run()将为您返回QFuture<T>。您应该可以在其上致电QFuture::waitForFinished

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