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();
}
実行時間が時間間隔より小さい場合
その後、予想どおり、タイマーは1秒ごとに着実に実行されます. 実行にかかった時間を考慮し、メソッド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
受信者が話し中だったために 1 クリックだけ逃した場合
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
また、時間の蓄積により次のクリックも見逃された場合でも機能します。ただし、各実行で見逃されるクリックが 1 つだけである場合に限ります。
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)
クリックが 2 回以上失敗した場合にのみ、問題が発生します。実行時間は最初の実行とは同期されませんが、ストールが終了した瞬間と同期されます。
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
結論
ストールがタイマー間隔の 2 倍より長くなる可能性がある場合は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
。本当なら写真を撮り、嘘なら何もしない。