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

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

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

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

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

ここでは、実務でよく使う
System.Timers.Timer と async/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秒後に一回だけ」というパターンもよくあります。
AutoReset を false にすると、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 を触ると例外になります。

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

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

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

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

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


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

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

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

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

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

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