C#でプログラミングをしていると、一定間隔で処理を実行したり、特定の時間に処理を行ったりする機能を利用したい場面があります。バックグラウンドでのデータ監視、定期的なAPI呼び出し、あるいはユーザーインターフェース(UI)の自動更新など、タイマー機能は様々なシステムで重要な役割を担っています。
しかし、C#には複数のタイマークラスが存在し、「どれを使えば良いのか分からない」と悩むこともあるでしょう。
この記事では、C#における主要なタイマークラスである System.Timers.Timer、System.Threading.Timer、そしてWindows Forms向けのSystem.Windows.Forms.Timerについて、それぞれの特徴、基本的な使い方、そして実践的な応用テクニックまでを分かりやすく解説します。
1.C# Timerの基礎知識
C#には複数のTimerクラスが用意されており、それぞれ特徴や用途が異なります。
この章では、主要なタイマークラスであるSystem.Timers.Timer、System.Threading.Timer、そしてWindows Formsアプリケーションでよく利用されるSystem.Windows.Forms.Timerについて、それぞれの基本的な特徴と使い方の概要を解説します。
System.Timers.Timerの特徴と使い方
System.Timers.Timerクラスは、サーバーベースのアプリケーションや、UIを持たないコンソールアプリケーションなどで汎用的に使用されるタイマーです。バックグラウンドスレッドで動作し、定期的な処理の実行に適しています。
基本的な使い方は以下のとおりです。
using System; using System.Timers;
{ static void Main(string[] args) { // Timerオブジェクトの作成 Timer myTimer = new Timer(); // Intervalを1000ミリ秒(1秒)に設定 myTimer.Interval = 1000; // イベントハンドラの登録 myTimer.Elapsed += OnTimedEvent; // タイマーの開始 (Enabledプロパティでも可) myTimer.Start(); }
private static void OnTimedEvent(Object source, ElapsedEventArgs e) { Console.WriteLine("タイマーイベントが発生しました: {0:HH:mm:ss.fff}", e.SignalTime); } } |
上記の例のように、まずTimerオブジェクトを生成して、Intervalプロパティで実行間隔をミリ秒単位で設定します。その後、Elapsedイベントに処理内容を記述したメソッドを登録し、EnabledプロパティをtrueにするかStart()メソッドを呼び出して開始します。
System.Threading.TimerとTimerCallbackの活用方法
System.Threading.Timer(ThreadingTimer)は、スレッドプールを利用した高パフォーマンスなタイマークラスです。イベントベースではなく、TimerCallbackデリゲートと呼ばれるコールバックメソッドを使用して処理を実行します。
System.Threading.Timerの使用例は以下のとおりです。
using System; using System.Threading;
{ private static Timer timer;
static void Main(string[] args) { var timerState = new TimerState { Counter = 0 }; // タイマーの生成と開始 timer = new Timer( callback: new TimerCallback(TimerTask), state: timerState, dueTime: 1000, // 最初の実行までの遅延(ミリ秒) period: 2000); // その後の実行間隔(ミリ秒)
timer.Dispose(); Console.WriteLine($"{DateTime.Now:HH:mm:ss.fff}: done."); }
// TimerCallbackデリゲートに適合するメソッド private static void TimerTask(object timerState) { Console.WriteLine($"{DateTime.Now:HH:mm:ss.fff}: starting a new callback."); var state = timerState as TimerState; Interlocked.Increment(ref state.Counter); }
class TimerState { public int Counter; } } |
System.Threading.Timerは、newキーワードでインスタンスを生成する際にTimerCallbackデリゲート、状態オブジェクト(省略可)、初回実行までの待機時間、繰り返し間隔を指定します。
Windows Forms向けTimer_Tickの実装方法
Windows Formsアプリケーションでは、System.Windows.Forms.Timerクラスを使用します。
System.Windows.Forms.Timerクラスの特徴は、Tickイベントが常にアプリケーションのUIスレッドで実行される点です。これにより、タイマーイベントハンドラ内からテキストボックスやラベルなどのUIコントロールを安全かつ直接的に操作できます。
System.Windows.Forms.Timerの使用例は以下のとおりです。
using System; using System.Windows.Forms;
{ public partial class Form1 : Form { private System.Windows.Forms.Timer timer; private int counter = 0;
public Form1() { InitializeComponent();
// タイマーの設定 timer = new Timer(); timer.Interval = 1000; // 1秒間隔 timer.Tick += Timer_Tick; timer.Enabled = true; // タイマーを開始 }
private void Timer_Tick(object sender, EventArgs e) { Console.WriteLine($"カウント: {counter}");
if (counter >= 10) { timer.Stop(); MessageBox.Show("タイマーが終了しました。"); } } } } |
System.Windows.Forms.Timerを使用するには、デザイナーからフォームにTimerコンポーネントを追加するか、コードでSystem.Windows.Forms.Timerオブジェクトを生成します。Intervalプロパティで間隔を設定し、Tickイベントハンドラを実装、Start()メソッドまたはEnabledプロパティでタイマーを開始します。
2.C# System.Timers.Timerの基本的な使い方
System.Timers.Timerは、汎用性が高く、よく使われるタイマークラスです。
この章では、System.Timers.Timerの基本的な使い方と主要なプロパティについて詳しく解説します。
C# Timer.Enabledプロパティによるタイマーの制御
System.Timers.Timerを開始・停止する基本的な方法として、Enabledプロパティによる制御が挙げられます。
Enabledプロパティをtrueに設定すると、タイマーが開始され、指定されたIntervalが経過するごとにElapsedイベントが発生します。Enabledプロパティをfalseに設定すると、タイマーは停止し、Elapsedイベントは発生しなくなります。
using System.Timers;
timer.Elapsed += OnTimedEvent; // OnTimedEventの定義は省略
timer.Enabled = true; // Start()メソッドと同等
timer.Enabled = false; // Stop()メソッドと同等 |
なお、Enabledプロパティをtrueに設定することはStartメソッドを呼び出すのと同じ効果があり、falseに設定することはStopメソッドを呼び出すのと同じです。
このように、EnabledプロパティまたはStart()/Stop()メソッドを使って、タイマーの動作を簡単に制御できます。
C# Elapsedイベントハンドラの活用方法
System.Timers.Timerクラスでは、タイマーイベント発生時に実行する処理をElapsedイベントハンドラで定義します。
ElapsedEventHandlerデリゲートは、object sender(イベントを発生させたタイマーオブジェクト)とElapsedEventArgs e(イベントに関する情報、特にイベント発生時刻 SignalTime を含む)の2つの引数を取ります。
処理の流れは以下のとおりです。
using System; using System.Timers;
{ static System.Timers.Timer eventTimer;
{ eventTimer = new System.Timers.Timer(1500); // 1.5秒間隔
eventTimer.Elapsed += HandleTimerElapsed;
eventTimer.Enabled = true; // タイマー開始
Console.ReadKey();
eventTimer.Dispose(); // リソースの解放 Console.WriteLine("タイマーを停止・破棄しました。"); }
private static void HandleTimerElapsed(Object source, ElapsedEventArgs e) { Console.WriteLine("イベント発生時刻: {0:HH:mm:ss.fff}", e.SignalTime); // ここに実行したい処理を記述 } } |
これにより、タイマーの処理をより柔軟に制御できます。
C# TimerのInterval設定
タイマーがElapsedイベントを発生させる間隔は、Intervalプロパティによってミリ秒単位で設定します。
例えば、3秒ごとにイベントを発生させたい場合はIntervalに3000を設定します。
using System.Timers;
intervalTimer.Interval = 3000;
intervalTimer.Start();
Console.ReadLine(); // ユーザー入力待ち intervalTimer.Dispose(); |
Intervalは0より大きい値を設定する必要があります。0以下の値を設定すると例外が発生します。
なおタイマーの精度はOSやシステム負荷に依存するため、Intervalで指定したミリ秒単位で厳密にイベントが発生するとは限りません。特に数ミリ秒単位の短い間隔では、ずれが大きくなる可能性があります。
関連記事
【C#】DateTime完全ガイド:初期化・文字列変換から年月日・時間・ミリ秒操作まで詳細解説
【C#】try-catchの使い方と例外処理|throwや複数例外の対応と使いどころを解説
3.C# System.Timers.Timerの応用テクニック
System.Timers.Timerの基本的な使い方を理解したところで、次はより実践的な応用テクニックを見ていきましょう。
この章では、1回だけの処理実行、経過時間の計測、終了待ち処理、スレッドセーフな実装など、実務で役立つテクニックを解説します。
C# Timerで1回だけ処理を実行する方法
タイマーを使って処理を1回だけ実行したい場合は、AutoResetプロパティをfalseに設定します。これにより、タイマーは最初のイベント発生後に自動的に停止します。
using System; using System.Timers;
{ private static Timer timer;
static void Main(string[] args) { // 1回だけ実行するタイマーの設定 timer = new Timer(3000); // 3秒後に実行 timer.Elapsed += OnTimedEvent; timer.AutoReset = false; // 繰り返し実行を無効化 timer.Enabled = true; // タイマーを開始
Console.WriteLine("3秒後に処理が1回だけ実行されます。"); Console.ReadLine();
timer.Dispose(); }
private static void OnTimedEvent(Object source, ElapsedEventArgs e) { Console.WriteLine($"処理が実行されました: {e.SignalTime:HH:mm:ss.fff}"); // ここに1回だけ実行したい処理を記述 } } |
このコードでは、AutoResetプロパティをfalseに設定することで、タイマーが最初のイベント発生後に自動的に停止します。
AutoResetプロパティを使用することで、指定時間後に一度だけ特定のタスクを実行するといったシナリオを簡単に実装できます。
C# Timerで経過時間を計測する方法
タイマーは「一定間隔で処理を起動する」ためのものであり、タイマー自体に「処理の開始から終了までにかかった時間」や「タイマーが起動してから経過した総時間」を直接計測する機能はありません。
タイマーを使って経過時間を計測するには、開始時間を記録して現在時間との差分を計算します。
using System; using System.Timers;
{ private static Timer timer; private static DateTime startTime;
static void Main(string[] args) { startTime = DateTime.Now;
timer = new Timer(1000); // 1秒間隔 timer.Elapsed += OnTimedEvent; timer.AutoReset = true; timer.Enabled = true;
Console.WriteLine("経過時間の計測を開始しました。Enterキーを押すと終了します。"); Console.ReadLine();
timer.Stop(); TimeSpan elapsed = DateTime.Now - startTime; Console.WriteLine($"合計経過時間: {elapsed.TotalSeconds:F2}秒");
timer.Dispose(); }
private static void OnTimedEvent(Object source, ElapsedEventArgs e) { TimeSpan elapsed = e.SignalTime - startTime; Console.WriteLine($"経過時間: {elapsed.TotalSeconds:F2}秒"); } } |
このコードでは、タイマーが開始された時点の時刻を記録し、Elapsedイベントが発生するたびに現在時刻との差分を計算して経過時間を表示しています。
ただ、DateTime.Nowを使った時間計測は、システムの時刻変更の影響を受けたり、Stopwatchに比べて精度が低い可能性があります。
このため、C#で経過時間を計測するときはSystem.Diagnostics.Stopwatchクラスを使用するのが一般的です。Stopwatchクラスは、高精度な時間計測機能を提供します。
TimerとStopwatchクラスを組み合わせて経過時間を計測する処理は以下のとおりです。
using System; using System.Diagnostics; using System.Timers;
{ private static Timer timer; private static Stopwatch stopwatch;
static void Main(string[] args) { stopwatch = new Stopwatch(); stopwatch.Start();
timer = new Timer(1000); timer.Elapsed += OnTimedEvent; timer.AutoReset = true; timer.Enabled = true;
Console.WriteLine("経過時間の計測を開始しました。Enterキーを押すと終了します。"); Console.ReadLine();
timer.Stop(); stopwatch.Stop(); Console.WriteLine($"合計経過時間: {stopwatch.Elapsed.TotalSeconds:F2}秒");
timer.Dispose(); }
private static void OnTimedEvent(Object source, ElapsedEventArgs e) { Console.WriteLine($"経過時間: {stopwatch.Elapsed.TotalSeconds:F2}秒"); } } |
Stopwatchクラスは高精度なタイマーを提供し、パフォーマンス計測などに適していると覚えておきましょう。
C# Timerの終了待ち処理について
アプリケーションを終了するときや、タイマーが不要になったときに、実行中のタイマー処理が完了するのを待ってから安全にリソースを解放したい場合があります。
このようにタイマーの処理が完了するまで待機する必要がある場合、単純にStop()メソッドを呼び出すだけでは、その時点で実行中のElapsedイベントハンドラの処理は中断されません。ワーカースレッドで実行中の処理が終わる前にメインスレッドが終了してしまうと、予期せぬ問題が発生する可能性があります。
タイマーの処理が完了するまで待機する方法としては、ManualResetEventを使用して同期を取る方法があります。
using System; using System.Threading; using System.Timers;
{ private static System.Timers.Timer timer; private static ManualResetEvent waitHandle = new ManualResetEvent(false);
static void Main(string[] args) { Console.WriteLine("タイマー処理を開始します...");
timer = new System.Timers.Timer(5000); // 5秒後に実行 timer.Elapsed += OnTimedEvent; timer.AutoReset = false; timer.Enabled = true;
Console.WriteLine("タイマー処理の完了を待機しています..."); waitHandle.WaitOne(); // シグナルが来るまでブロック
Console.WriteLine("タイマー処理が完了しました。"); timer.Dispose(); waitHandle.Dispose(); }
private static void OnTimedEvent(Object source, ElapsedEventArgs e) { Console.WriteLine($"タイマー処理を実行中: {e.SignalTime:HH:mm:ss.fff}");
// 実際の処理をここに記述 Thread.Sleep(2000); // 処理に時間がかかると仮定
Console.WriteLine("タイマー処理が完了しました。"); waitHandle.Set(); // 待機しているスレッドに通知 } } |
このコードでは、ManualResetEventを使用してタイマー処理の完了を待機しています。タイマー処理が完了すると、Set()メソッドを呼び出してメインスレッドに通知します。
C# Timerのスレッドセーフな実装方法
System.Timers.TimerのElapsedイベントハンドラは、デフォルトではスレッドプールのワーカースレッドで実行されます。
つまりタイマーはマルチスレッド環境で動作するため、共有リソースへのアクセスには注意が必要です。複数のスレッドが同時に同じ変数やデータ構造を変更しようとすると、データの不整合や競合状態が発生する可能性があります。
これを防ぐためには、排他制御が必要です。最も一般的な方法はlockステートメントを使用することです。
using System; using System.Threading; using System.Timers;
{ private static System.Timers.Timer timer; private static int counter = 0; private static readonly object lockObject = new object();
static void Main(string[] args) { timer = new System.Timers.Timer(100); // 0.1秒間隔 timer.Elapsed += OnTimedEvent; timer.AutoReset = true; timer.Enabled = true;
Console.WriteLine("スレッドセーフなタイマー処理を実行中..."); Console.ReadLine();
timer.Stop(); Console.WriteLine($"最終カウント: {counter}"); timer.Dispose(); }
private static void OnTimedEvent(Object source, ElapsedEventArgs e) { // lockを使用して共有リソースへのアクセスを同期 lock (lockObject) { counter++; Console.WriteLine($"カウント: {counter}, スレッドID: {Thread.CurrentThread.ManagedThreadId}"); } } } |
lockステートメントは、指定されたオブジェクトをロックし、他のスレッドが同じオブジェクトでlockしようとした場合に待機させます。これにより、lockブロック内のコードは一度に一つのスレッドによってのみ実行されることが保証されます。
関連記事
【C#】for文の使い方|foreach文との違いや配列・Listとの連携方法をサンプルコード付で解説
【C#】Listと配列との違いや初期化・削除・要素数取得・型指定なしの使い方を紹介
4.まとめ
本記事では、C#におけるタイマー機能、特にSystem.Timers.Timerクラスを中心に、その基本的な使い方から応用テクニックまでを解説しました。
C#にはSystem.Timers.Timer、System.Threading.Timer、System.Windows.Forms.Timer というさまざまなタイマークラスがあります。用途に応じて適切なものを選択することが重要です。
System.Timers.Timerは汎用的なタイマークラスで、バックグラウンドスレッドで動作します。一方、System.Threading.TimerはTimerCallbackを使用した高パフォーマンスなタイマー処理が可能で、スレッドプールを効率的に活用できます。Windows Forms向けのTimerはUIスレッドで動作するため、UI要素を直接操作できる利点があります。
C#のTimerは、定期的なデータ更新やバックグラウンド処理、ユーザー入力の監視など、多くのシナリオで活用できる機能です。
本記事が皆様にとって少しでもお役に立てますと幸いです。
「フリーランスボード」は、数多くのフリーランスエージェントが掲載するITフリーランスエンジニア・ITフリーランス向けの案件・求人を一括検索できるサイトです。
開発環境、職種、単価、稼働形態、稼働日数など様々な条件から、あなたに最適なフリーランス案件・求人を簡単に見つけることができます。
単価アップを目指す方や、自分の得意なスキルを活かせる案件に参画したい方は、ぜひ「フリーランスボード」をご利用ください。
自身に最適なフリーランスエージェントを探したい方はこちらよりご確認いただけます。