C#なんぞ

C#に関するあれこれ。現在は「Raspberry PiをC#でプログラミング!」しようとしています!

より正確なタイマーを!

結構遅れるTask.Delayメソッド

時間待ちにTask.Delayメソッドを使うと、どうも指定した時間より数十ミリ秒長く待っているようです。

(前回のコードでは、AutoResetEvent.WaitOne/ManualResetEvent.WaitOneメソッドを使っていますが、たぶん同じように遅れますね)

csharpwhat.hatenablog.com

Task.Delayの呼び出しの間にSense HATに対するI/Oをしていますので、I/Oが遅いのかもしれません。

あるいは、そもそもRaspberry PiWindows 10 IoTの、タイマー分解能の限界かもしれません。

どちらが原因にせよ、繰り返し時間待ちがあると誤差が降り積もって、どんどん遅れてしまいます!

より正確な時間待ち!

ということで、より正確に繰り返し時間待ちができるクラスを作ってみました。

using System;
using System.Threading.Tasks;

// 待ち時間を指定して繰り返し待つためのタイマー
internal class IntervalTimer
{
    // 時間待ち明けの予定時刻
    private DateTime BaseTime { get; set; }

    // コンストラクター
    public IntervalTimer() => Reset();

    // 時間待ち明けの予定時刻を現在時刻にセットしなおす
    public void Reset() => BaseTime = DateTime.Now;

    // 引数で指定した時間、待つ
    public async Task Delay(TimeSpan timeSpan)
    {
        // 時間待ち明けの予定時刻との差を引数から減じ、待ち時間を求める
        TimeSpan diff = timeSpan - (DateTime.Now - BaseTime);
        // 時間待ち明けの予定時刻をセットする
        BaseTime += timeSpan;
        // 待つ
        if (diff > TimeSpan.Zero)
            await Task.Delay(diff);
    }
}

このクラスのインスタンスを作り、Task.Delayメソッドの代わりにIntervalTimer.Delayを呼び出して、時間待ちします。

個々の時間待ちは依然として遅れますが、遅れが降り積もっていくことは避けられます!

いろいろ難しい!