После некоторых экспериментов я подробно описал, как 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)
Затем следующий слот вызывается сразу же после завершения задержки, но последующие вызовы по-прежнему кратны 1000 мс:
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
. Если это правда, сделайте снимок, если это ложь, ничего не делайте.
person
Boris Dalstein
schedule
09.09.2013