C# Tips | 日付・時間処理:タイマー処理

C# C#
スポンサーリンク

はじめに 「タイマー処理」は“時間を味方につける仕組み”

「5秒ごとに処理したい」「1分後にだけ一回だけ実行したい」「一定間隔でバッチを回したい」
こういう“時間をトリガーにした処理”をまとめて「タイマー処理」と呼びます。

C# にはいくつかのタイマーがあり、用途に応じて使い分けますが、
初心者のうちは「何秒ごとに処理する」「一回だけ遅延実行する」の2パターンを押さえれば十分です。

ここでは、実務でよく使う
System.Timers.Timerasync/await + Task.Delay を中心に、
「どう書くか」だけでなく「どこでハマりやすいか」までかみ砕いて説明します。


基本形:System.Timers.Timer で「一定間隔で処理する」

5秒ごとにログを出すタイマー

まずは「一定間隔で同じ処理を繰り返す」一番典型的なパターンです。

using System;
using System.Timers;

public class TimerSample
{
    private readonly Timer _timer;

    public TimerSample()
    {
        _timer = new Timer();
        _timer.Interval = 5000; // ミリ秒。ここでは 5秒
        _timer.AutoReset = true; // 繰り返す
        _timer.Elapsed += OnElapsed;
    }

    public void Start()
    {
        _timer.Start();
    }

    public void Stop()
    {
        _timer.Stop();
    }

    private void OnElapsed(object? sender, ElapsedEventArgs e)
    {
        Console.WriteLine($"[{DateTime.Now:HH:mm:ss}] タイマー発火");
    }
}
C#

使い方のイメージはこうです。

var sample = new TimerSample();
sample.Start();

Console.WriteLine("Enter を押すと終了します。");
Console.ReadLine();

sample.Stop();
C#

重要なのは次の3点です。

タイマー本体は Timer クラスのインスタンスとして保持する(ローカル変数にするとすぐ GC される)。
Interval はミリ秒単位で指定する。
Elapsed イベントに「タイマーが鳴ったときに実行したい処理」を書く。

この形を一度手で書いてみると、「タイマーってこういう構造なんだ」という感覚がつかめます。


一回だけ実行したいときのタイマー(ワンショット)

10秒後に一度だけ処理したい

「5秒ごと」ではなく「10秒後に一回だけ」というパターンもよくあります。
AutoResetfalse にすると、1回だけ発火して止まるタイマーになります。

public class OneShotTimerSample
{
    private readonly Timer _timer;

    public OneShotTimerSample()
    {
        _timer = new Timer();
        _timer.Interval = 10000; // 10秒
        _timer.AutoReset = false; // 一回だけ
        _timer.Elapsed += OnElapsed;
    }

    public void Start()
    {
        _timer.Start();
    }

    private void OnElapsed(object? sender, ElapsedEventArgs e)
    {
        Console.WriteLine("10秒経過しました。1回だけ実行されます。");
    }
}
C#

このように、「繰り返すかどうか」は AutoReset で制御します。
「一回だけ遅延実行したい」処理を、タイマーに任せるイメージです。


async/await を使ったタイマー的処理(Task.Delay)

シンプルに「待ってから実行」したい場合

コンソールアプリやバッチ処理などでは、
System.Timers.Timer を使わずに、Task.Delay で「待ってから実行」する書き方もよく使います。

using System;
using System.Threading.Tasks;

public class DelaySample
{
    public static async Task RunAsync()
    {
        Console.WriteLine("3秒待ちます…");
        await Task.Delay(TimeSpan.FromSeconds(3));
        Console.WriteLine("3秒経ちました。処理を実行します。");
    }
}
C#

呼び出し側はこうです。

await DelaySample.RunAsync();
C#

「一定間隔で繰り返す」場合も、ループと組み合わせれば書けます。

public static async Task RunLoopAsync()
{
    while (true)
    {
        Console.WriteLine($"[{DateTime.Now:HH:mm:ss}] ループ処理");
        await Task.Delay(TimeSpan.FromSeconds(5));
    }
}
C#

Task.Delay の良いところは、「コードの流れがそのまま時間の流れになる」ことです。
イベントハンドラを使わずに書けるので、初心者にはこちらのほうが理解しやすいことも多いです。


実務でハマりやすいポイント

1. タイマーのインスタンスをすぐに捨てない

new Timer(...) をメソッドのローカル変数にしてしまうと、
メソッドを抜けたあとにガベージコレクションで回収され、タイマーが止まってしまうことがあります。

必ずフィールドや、長生きするオブジェクトの中で保持するようにしてください。

2. UI アプリでは UI スレッドに戻す必要がある

WPF や WinForms などの UI アプリで System.Timers.Timer を使うと、
Elapsed は別スレッドで呼ばれます。
その中で直接 UI を触ると例外になります。

その場合は、DispatcherSynchronizationContext を使って、
UI スレッドに処理を投げ直す必要があります。

初心者のうちは、
「UI なら DispatcherTimer(WPF)や System.Windows.Forms.Timer を使う」
「バックグラウンド処理なら System.Timers.TimerTask.Delay を使う」
とざっくり分けて覚えておくと混乱しにくいです。

3. タイマーは止める・破棄する

長時間動き続けるサービスやバッチでは、
不要になったタイマーを Stop() したり Dispose() したりしないと、
無駄にイベントが飛び続けたり、リソースリークの原因になります。

「開始したら、どこで止めるか/破棄するか」をセットで考える癖をつけておくと、
タイマー絡みのバグを減らせます。


まとめ 「タイマー処理ユーティリティ」は“時間をトリガーにする共通パターン”

タイマー処理は、
「一定間隔で処理する」「少し待ってから処理する」という、
業務システムで何度も出てくるパターンです。

押さえておきたいポイントを整理すると、こうなります。

System.Timers.Timer は「Interval」「AutoReset」「Elapsed」の3点セットで覚える。
一回だけ実行したいときは AutoReset = false にする。
Task.Delay は「待ってから続きの処理をする」書き方として、とても読みやすい。
タイマーのインスタンスはフィールドなどで保持し、勝手に GC されないようにする。
UI アプリでは UI 用のタイマー(DispatcherTimer など)か、UI スレッドへの戻し方を意識する。

このあたりが体に馴染んでくると、
「時間が絡む処理」を怖がらずに、
“業務ロジックに合わせて時間をコントロールできるプログラマー”になっていけます。

タイトルとURLをコピーしました