参考文章:Qt QTimer::singleShot问题及用法
1. 问题描述
QTimer::singleShot定时器事件超时,如果此时类内对象已经被回收,定时器事件调用已经释放的类内资源时会引起崩溃。
这通常是因为定时器的回调函数(槽函数)在执行时可能会导致事件循环被阻塞,进而导致定时器无法正常触发,或者在高频率调用时,定时器的数量迅速增加,造成资源消耗过大。
QTimer::singleShot(100, this, [=]() {
LOG(INFO) << QString::fromLocal8Bit("____QTimer::singleShot testtest____").toStdString();
LOG(INFO) << QString::number(i).toStdString();
});
2. 百分百复现
可使用以下代码:
在while语句中不停使用QTimer::singleShot。
while (1)
{
QTimer::singleShot(100, this, [=]() {
LOG(INFO) << QString::fromLocal8Bit("____QTimer::singleShot testtest____").toStdString();
LOG(INFO) << QString::number(i).toStdString();
});
}
报错如图所示:
3. 解决方法1:使用QCoreApplication::processEvents()
while (1)
{
QCoreApplication::processEvents();
QTimer::singleShot(100, this, [=]() {
LOG(INFO) << QString::fromLocal8Bit("____QTimer::singleShot testtest____").toStdString();
LOG(INFO) << QString::number(i).toStdString();
});
}
QCoreApplication::processEvents()
是 Qt 框架中的一个函数,用于处理事件循环中的待处理事件。它的主要作用是使应用程序能够在某些情况下处理事件,比如用户输入、定时器超时等,而不会阻塞程序的执行。
QCoreApplication::processEvents()
的作用
处理事件:当调用
processEvents()
时,Qt 会处理所有待处理的事件,包括定时器事件。这意味着即使在某个长时间运行的操作中,定时器也有机会被触发,从而避免了定时器的回调函数无法执行的问题。避免阻塞:如果在槽函数中执行了耗时的操作,调用
processEvents()
可以让事件循环继续运行,处理其他事件,防止应用程序因为一个长时间运行的操作而完全无响应。释放资源:通过让事件循环处理其他事件,
processEvents()
可以帮助释放一些资源,避免因为定时器未能及时触发而造成的资源泄漏。
4. 解决方法2:用定时器代替QTimer::singleShot
while (1)
{
QTimer *t = new QTimer;
connect(t, &QTimer::timeout, [=]() {
LOG(INFO) << QString::fromLocal8Bit("____QTimer::singleShot testtest____").toStdString();
LOG(INFO) << QString::number(i).toStdString();
});
t->setSingleShot(true);
t->start(50);
}
或
while (1)
{
QTimer *t = new QTimer;
connect(t, &QTimer::timeout, [=]() {
t->stop();
t->deleteLater();
LOG(INFO) << QString::fromLocal8Bit("____QTimer::singleShot testtest____").toStdString();
LOG(INFO) << QString::number(i).toStdString();
});
t->start(50);
}
5. 解决方法3:设置interval为0
while (1)
{
QTimer *t = new QTimer;
t->singleShot(0, this,connect(t, &QTimer::timeout, [=]() {
LOG(INFO) << QString::fromLocal8Bit("____QTimer::singleShot testtest____").toStdString();
LOG(INFO) << QString::number(i).toStdString();
});
}
或
while (1)
{
QTimer::singleShot(0, this,connect(t, &QTimer::timeout, [=]() {
LOG(INFO) << QString::fromLocal8Bit("____QTimer::singleShot testtest____").toStdString();
LOG(INFO) << QString::number(i).toStdString();
});
}
虽然这个方法可以使其不报错,但是因为设置interval为0,对于时间间隔为0的事件,甚至连QSingleShotTimer都不需要创建,而是直接用invokeMethod去调用相应的slot,所以定时器不算响应了。
参考文章:QTimer源码分析(以Windows下实现为例)_qtimer::singleshot源码及分析-CSDN博客