C++ Builder XE4, 10.2 Tokyo > TTimer > 10秒ごとの処理 > 1. TTimer(1秒) + IncSecond(Now())での期限 | 2. TTimerのInterval指定 > 4分弱の間に1の結果に3秒程度のずれが見られる > 修正した


動作環境
C++ Builder XE4
RAD Studio 10.2 Tokyo Update 2 (追記: 2018/01/05)

処理内容

TTimerを使ったインターバル処理。

  1. TTimer(1秒) + IncSecond()での期限
  2. TTimerのみ使用
    • Intervalプロパティに期限までの時間(msec)を指定

失敗編

実装

Unit1.h
//---------------------------------------------------------------------------

#ifndef Unit1H
#define Unit1H
//---------------------------------------------------------------------------
#include <System.Classes.hpp>
#include <Vcl.Controls.hpp>
#include <Vcl.StdCtrls.hpp>
#include <Vcl.Forms.hpp>
#include <Vcl.ExtCtrls.hpp>
//---------------------------------------------------------------------------
class TForm1 : public TForm
{
__published:    // IDE で管理されるコンポーネント
    TTimer *Timer1sec;
    TTimer *TimerAlarm;
    TButton *B_start;
    TMemo *Memo1;
    void __fastcall B_startClick(TObject *Sender);
    void __fastcall Timer1secTimer(TObject *Sender);
    void __fastcall TimerAlarmTimer(TObject *Sender);
private:    // ユーザー宣言
public:     // ユーザー宣言
    __fastcall TForm1(TComponent* Owner);
};
//---------------------------------------------------------------------------
extern PACKAGE TForm1 *Form1;
//---------------------------------------------------------------------------
#endif
Unit1.cpp
//---------------------------------------------------------------------------

#include <vcl.h>
#pragma hdrstop

#include <DateUtils.hpp>
#include "Unit1.h"
//---------------------------------------------------------------------------
#pragma package(smart_init)
#pragma resource "*.dfm"
TForm1 *Form1;
//---------------------------------------------------------------------------


static const int kWaitSecond = 10;

static TDateTime m_timeLimit;

__fastcall TForm1::TForm1(TComponent* Owner)
    : TForm(Owner)
{
    Timer1sec->Enabled = false;
    TimerAlarm->Enabled = false;
}
//---------------------------------------------------------------------------
void __fastcall TForm1::B_startClick(TObject *Sender)
{
    // 1. 1sec timer
    Timer1sec->Enabled  = false;
    Timer1sec->Interval = 1000; // msec
    Timer1sec->Enabled  = true;
    m_timeLimit = IncSecond(Now(), kWaitSecond);

    // 2. Alarm timer
    TimerAlarm->Enabled = false;
    TimerAlarm->Interval = kWaitSecond * 1000; // msec
    TimerAlarm->Enabled = true;
}
//---------------------------------------------------------------------------

void __fastcall TForm1::Timer1secTimer(TObject *Sender)
{
    if (Now() >= m_timeLimit) {
        m_timeLimit = IncSecond(Now(), kWaitSecond);
        Memo1->Lines->Add(L"1sec timer: " + Now().DateTimeString());
    }
}
//---------------------------------------------------------------------------

void __fastcall TForm1::TimerAlarmTimer(TObject *Sender)
{
    Memo1->Lines->Add(L"alarm timer: " + Now().DateTimeString());
}
//---------------------------------------------------------------------------

結果

結果
Memo1
alarm timer: 2017/10/17 19:16:44
1sec timer: 2017/10/17 19:16:45
alarm timer: 2017/10/17 19:16:54
1sec timer: 2017/10/17 19:16:55
alarm timer: 2017/10/17 19:17:04
1sec timer: 2017/10/17 19:17:05
alarm timer: 2017/10/17 19:17:15
1sec timer: 2017/10/17 19:17:15
alarm timer: 2017/10/17 19:17:25
1sec timer: 2017/10/17 19:17:25
alarm timer: 2017/10/17 19:17:35
1sec timer: 2017/10/17 19:17:35
alarm timer: 2017/10/17 19:17:45
1sec timer: 2017/10/17 19:17:45
alarm timer: 2017/10/17 19:17:55
1sec timer: 2017/10/17 19:17:56
alarm timer: 2017/10/17 19:18:05
1sec timer: 2017/10/17 19:18:06
alarm timer: 2017/10/17 19:18:15
1sec timer: 2017/10/17 19:18:16
alarm timer: 2017/10/17 19:18:25
1sec timer: 2017/10/17 19:18:26
alarm timer: 2017/10/17 19:18:35
1sec timer: 2017/10/17 19:18:36
alarm timer: 2017/10/17 19:18:45
1sec timer: 2017/10/17 19:18:46
alarm timer: 2017/10/17 19:18:55
1sec timer: 2017/10/17 19:18:56
alarm timer: 2017/10/17 19:19:05
1sec timer: 2017/10/17 19:19:07
alarm timer: 2017/10/17 19:19:15
1sec timer: 2017/10/17 19:19:17
alarm timer: 2017/10/17 19:19:25
1sec timer: 2017/10/17 19:19:27
alarm timer: 2017/10/17 19:19:35
1sec timer: 2017/10/17 19:19:37
alarm timer: 2017/10/17 19:19:45
1sec timer: 2017/10/17 19:19:47
alarm timer: 2017/10/17 19:19:55
1sec timer: 2017/10/17 19:19:57
alarm timer: 2017/10/17 19:20:05
1sec timer: 2017/10/17 19:20:07
alarm timer: 2017/10/17 19:20:15
1sec timer: 2017/10/17 19:20:18
alarm timer: 2017/10/17 19:20:25
1sec timer: 2017/10/17 19:20:28
  1. TTimer(1秒) + IncSecond()での期限
    • => 4分弱で3秒のずれが見られた
  2. TTimerのみ使用
    • Intervalプロパティに期限までの時間(msec)を指定
    • => 4分弱で1秒以内のずれとなった

IncSecond()した時にmsec単位で誤差があるのだろうか?

1時間インターバル

alarm timer: 2017/10/17 20:35:03
1sec timer: 2017/10/17 20:35:04
alarm timer: 2017/10/17 21:35:03
1sec timer: 2017/10/17 21:35:04
alarm timer: 2017/10/17 22:35:03
1sec timer: 2017/10/17 22:35:05
alarm timer: 2017/10/17 23:35:03
1sec timer: 2017/10/17 23:35:06
alarm timer: 2017/10/18 0:35:03
1sec timer: 2017/10/18 0:35:07
alarm timer: 2017/10/18 1:35:03
1sec timer: 2017/10/18 1:35:08
alarm timer: 2017/10/18 2:35:03
1sec timer: 2017/10/18 2:35:08
alarm timer: 2017/10/18 3:35:03
1sec timer: 2017/10/18 3:35:09
alarm timer: 2017/10/18 4:35:03
1sec timer: 2017/10/18 4:35:10
alarm timer: 2017/10/18 5:35:03
1sec timer: 2017/10/18 5:35:10
alarm timer: 2017/10/18 6:35:03
1sec timer: 2017/10/18 6:35:11
alarm timer: 2017/10/18 7:35:03
1sec timer: 2017/10/18 7:35:12

やはり1sec timerの方でずれが発生している。
TTimer->Interval = 1000msecとはしているが、このインターバルには誤差が含まれる。その誤差が蓄積されて上記のようなずれが発生するのかもしれない。

11時間で8秒程度のずれ。11時間=39600秒。
8 / 39600 = 0.000202。

IncSecond()をNow()と組み合わせているのが問題である。

修正

IncSecond(Now())ではなく、IncSecond(timeLimit)とした。
こうすることで、TTimerのOnTimerコール時間の誤差とは関係なく期限を設定できる。

Unit1.cpp
//---------------------------------------------------------------------------

#include <vcl.h>
#pragma hdrstop

#include <DateUtils.hpp>
#include "Unit1.h"
//---------------------------------------------------------------------------
#pragma package(smart_init)
#pragma resource "*.dfm"
TForm1 *Form1;
//---------------------------------------------------------------------------

/*
v0.2 Oct. 18, 2017
    - refactor: rename [m_timeLimit] to [s_timeLimit]
    - fix bug: IncSecond() with Now() accumulated the deviation of the time
v0.1 Oct. 17, 2017
    - add two timers
        + 1. TTimer(1sec) + time limit
        + 2. TTimer->Interval
*/

static const int kWaitSecond = 10;  // 10

static TDateTime s_timeLimit;

__fastcall TForm1::TForm1(TComponent* Owner)
    : TForm(Owner)
{
    Timer1sec->Enabled = false;
    TimerAlarm->Enabled = false;
}
//---------------------------------------------------------------------------
void __fastcall TForm1::B_startClick(TObject *Sender)
{
    // 1. 1sec timer
    Timer1sec->Enabled  = false;
    Timer1sec->Interval = 1000; // msec
    Timer1sec->Enabled  = true;
    s_timeLimit = IncSecond(Now(), kWaitSecond);

    // 2. Alarm timer
    TimerAlarm->Enabled = false;
    TimerAlarm->Interval = kWaitSecond * 1000; // msec
    TimerAlarm->Enabled = true;
}
//---------------------------------------------------------------------------

void __fastcall TForm1::Timer1secTimer(TObject *Sender)
{
    if (Now() >= s_timeLimit) {
        //m_timeLimit = IncSecond(Now(), kWaitSecond);   // v0.1
        s_timeLimit = IncSecond(s_timeLimit, kWaitSecond);  // v0.2
        Memo1->Lines->Add(L"1sec timer: " + Now().DateTimeString());
    }
}
//---------------------------------------------------------------------------

void __fastcall TForm1::TimerAlarmTimer(TObject *Sender)
{
    Memo1->Lines->Add(L"alarm timer: " + Now().DateTimeString());
}
//---------------------------------------------------------------------------

run
alarm timer: 2017/10/18 8:49:39
1sec timer: 2017/10/18 8:49:39
alarm timer: 2017/10/18 8:49:49
1sec timer: 2017/10/18 8:49:49
alarm timer: 2017/10/18 8:49:59
1sec timer: 2017/10/18 8:49:59
alarm timer: 2017/10/18 8:50:09
1sec timer: 2017/10/18 8:50:09
alarm timer: 2017/10/18 8:50:19
1sec timer: 2017/10/18 8:50:19
alarm timer: 2017/10/18 8:50:29
1sec timer: 2017/10/18 8:50:29
alarm timer: 2017/10/18 8:50:39
1sec timer: 2017/10/18 8:50:39
alarm timer: 2017/10/18 8:50:49
1sec timer: 2017/10/18 8:50:49
alarm timer: 2017/10/18 8:50:59
1sec timer: 2017/10/18 8:50:59
alarm timer: 2017/10/18 8:51:09
1sec timer: 2017/10/18 8:51:09
alarm timer: 2017/10/18 8:51:19
1sec timer: 2017/10/18 8:51:19
alarm timer: 2017/10/18 8:51:29
1sec timer: 2017/10/18 8:51:29
alarm timer: 2017/10/18 8:51:39
1sec timer: 2017/10/18 8:51:39
alarm timer: 2017/10/18 8:51:49
1sec timer: 2017/10/18 8:51:49
1sec timer: 2017/10/18 8:51:59
alarm timer: 2017/10/18 8:51:59
alarm timer: 2017/10/18 8:52:09
1sec timer: 2017/10/18 8:52:09
alarm timer: 2017/10/18 8:52:19
1sec timer: 2017/10/18 8:52:19
alarm timer: 2017/10/18 8:52:29
1sec timer: 2017/10/18 8:52:29
alarm timer: 2017/10/18 8:52:39
1sec timer: 2017/10/18 8:52:39
alarm timer: 2017/10/18 8:52:49
1sec timer: 2017/10/18 8:52:49
alarm timer: 2017/10/18 8:52:59
1sec timer: 2017/10/18 8:52:59
1sec timer: 2017/10/18 8:53:09
alarm timer: 2017/10/18 8:53:09
alarm timer: 2017/10/18 8:53:19
1sec timer: 2017/10/18 8:53:19
alarm timer: 2017/10/18 8:53:29
1sec timer: 2017/10/18 8:53:29
alarm timer: 2017/10/18 8:53:39
1sec timer: 2017/10/18 8:53:39

ずれがなくなった。

疲れた頭でコーディングするとこういう失敗をする。
さらにstatic変数をm_定義している失敗も見つけたので修正しておいた。