我有一个对话框,该对话框在初始化时会执行耗时的操作。我将此操作包装到一个异步函数中,以免冻结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
接口的原因。
答案 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。至于取消异步操作,无法通过QtConcurrent
或QThread
方法正确取消它。
有QThread::terminate()方法,但来自文档:
...警告:此功能很危险,不建议使用。的 线程可以在其代码路径中的任何位置终止...
因此,您必须在getWeather()
函数内部实现一些“取消”标志,或者按照上面的说明进行操作。
答案 1 :(得分:0)
QtConcurrent::run()
将为您返回QFuture<T>
。您应该可以在其上致电QFuture::waitForFinished
。
另一件事是,不应允许您从另一个线程调用QLabel::setText
,而应使用QMetaObject::invokeMethod
或发出信号。