我有一个对话框,该对话框在初始化时会执行耗时的操作。我将此操作包装到一个异步函数中,以免冻结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
),以便在关闭对话框时正确取消异步功能。
请注意,实际代码与读取一堆文件有关,not与网络有关,这就是为什么不使用异步QNetworkRequest
接口的原因。
// 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()));
}
ps.s。至于取消异步操作,无法通过QtConcurrent
或QThread
方法正确取消它。
有QThread::terminate()方法,但来自文档:
...警告:此功能很危险,不建议使用。的线程可以在其代码路径中的任何位置终止...
因此,您必须在getWeather()
函数内部实现一些“取消”标志,或按照上面的描述进行操作。
QtConcurrent::run()
将返回一个QFuture<T>
。您应该可以在其上调用QFuture::waitForFinished
。
另一件事是,您不应被允许从另一个线程调用QLabel::setText
,而应使用QMetaObject::invokeMethod
或发出信号。