经过一些实验后,我在这里详细介绍QTimer
了接收器忙时的行为方式。
这里是实验源代码:(添加QT += testlib
到项目文件中)
#include <QtGui>
#include <QtDebug>
#include <QTest>
struct MyWidget: public QWidget
{
QList<int> n; // n[i] controls how much time the i-th execution takes
QElapsedTimer t; // measure how much time has past since we launch the app
MyWidget()
{
// The normal execution time is 200ms
for(int k=0; k<100; k++) n << 200;
// Manually add stalls to see how it behaves
n[2] = 900; // stall less than the timer interval
// Start the elapsed timer and set a 1-sec timer
t.start();
startTimer(1000); // set a 1-sec timer
}
void timerEvent(QTimerEvent *)
{
static int i = 0; i++;
qDebug() << "entering:" << t.elapsed();
qDebug() << "sleeping:" << n[i]; QTest::qSleep(n[i]);
qDebug() << "leaving: " << t.elapsed() << "\n";
}
};
int main(int argc, char ** argv)
{
QApplication app(argc, argv);
MyWidget w;
w.show();
return app.exec();
}
当执行时间小于时间间隔时
然后正如预期的那样,计时器每秒钟稳定地运行一次。它确实考虑了执行花费了多少时间,然后该方法timerEvent
总是以 1000 毫秒的倍数开始:
entering: 1000
sleeping: 200
leaving: 1201
entering: 2000
sleeping: 900
leaving: 2901
entering: 3000
sleeping: 200
leaving: 3201
entering: 4000
sleeping: 200
leaving: 4201
因接收方忙而错过一键点击时
n[2] = 1500; // small stall (longer than 1sec, but less than 2sec)
然后,在停顿结束后立即调用下一个槽,但后续调用仍然是 1000ms 的倍数:
entering: 1000
sleeping: 200
leaving: 1200
entering: 2000
sleeping: 1500
leaving: 3500 // one timer click is missed (3500 > 3000)
entering: 3500 // hence, the following execution happens right away
sleeping: 200
leaving: 3700 // no timer click is missed (3700 < 4000)
entering: 4000 // normal execution times can resume
sleeping: 200
leaving: 4200
entering: 5000
sleeping: 200
leaving: 5200
如果由于时间的累积,也错过了以下点击,它也可以工作,只要每次执行时只错过一次点击:
n[2] = 1450; // small stall
n[3] = 1450; // small stall
输出:
entering: 1000
sleeping: 200
leaving: 1201
entering: 2000
sleeping: 1450
leaving: 3451 // one timer click is missed (3451 > 3000)
entering: 3451 // hence, the following execution happens right away
sleeping: 1450
leaving: 4901 // one timer click is missed (4901 > 4000)
entering: 4902 // hence, the following execution happens right away
sleeping: 200
leaving: 5101 // one timer click is missed (5101 > 5000)
entering: 5101 // hence, the following execution happens right away
sleeping: 200
leaving: 5302 // no timer click is missed (5302 < 6000)
entering: 6000 // normal execution times can resume
sleeping: 200
leaving: 6201
entering: 7000
sleeping: 200
leaving: 7201
当因为接收方很忙而错过了一次以上的点击时
n[2] = 2500; // big stall (more than 2sec)
如果错过了两次或多次点击,则只会出现问题。执行时间与第一次执行不同步,而是与停顿完成的那一刻同步:
entering: 1000
sleeping: 200
leaving: 1200
entering: 2000
sleeping: 2500
leaving: 4500 // two timer clicks are missed (3000 and 4000)
entering: 4500 // hence, the following execution happens right away
sleeping: 200
leaving: 4701
entering: 5500 // and further execution are also affected...
sleeping: 200
leaving: 5702
entering: 6501
sleeping: 200
leaving: 6702
结论
如果停顿时间可能比定时器间隔的两倍长,则必须使用Digikata的解决方案,否则就不需要了,上面的简单实现效果很好。在这种情况下,您宁愿具有以下行为:
entering: 1000
sleeping: 200
leaving: 1200
entering: 2000
sleeping: 1500
leaving: 3500 // one timer click is missed
entering: 4000 // I don't want t execute the 3th execution
sleeping: 200
leaving: 4200
然后你仍然可以使用简单的实现,只需检查enteringTime < expectedTime + epsilon
. 如果是真的,就拍照,如果是假的,什么也不做。