はじめに 「タイマー処理」は“時間を味方につける仕組み”
「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 スレッドへの戻し方を意識する。
このあたりが体に馴染んでくると、
「時間が絡む処理」を怖がらずに、
“業務ロジックに合わせて時間をコントロールできるプログラマー”になっていけます。
