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。至于取消异步操作,用QtConcurrent
或QThread
方法无法正确取消。
有 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" 标志,或者按照上面写的那样做。
背景
我有一个对话框,它在初始化时运行一个耗时的操作。为了不冻结 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。至于取消异步操作,用QtConcurrent
或QThread
方法无法正确取消。
有 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" 标志,或者按照上面写的那样做。