はじめに
一定の間隔を置いて処理を実行させるためにはタイマーを使いますC#の標準ライブラリには5種類(4種類)のタイマーがあります。
その前に
実行スレッド
一定間隔毎に実行される処理をハンドラ内に定義します。これがどのスレッドで実行されるかはタイマー及びその設定に依存しますGUIのコンポーネントはUIスレッド以外から操作ができないので、非UIスレッドで実行させる場合はディスパッチする必要があります
一定間隔とは
実行間隔の指定は、その間隔毎に実行されることではなく、その指定間隔以上の間をあけて実行されることを意味しますつまり、1分間隔と指定した場合、ハンドラが再度実行されるのは丁度一分後という保証はないが、一分以内に実行されることはないことは保証されます
(公式文章の機械翻訳は誤訳ですが原文の英語を読むとよくわかります)
それタイマー?
例えば、30分毎に実行する必要がある処理の場合、それはタイマー起動ではなく、もしかしたらスケジューラで起動する方が正解かもしれませんタイマーの種類
System.Windows.Forms.Timer
・名前空間にあるようにFormを持つUI層での使用が想定される・TickイベントハンドラはUIスレッドで実行される
System.Windows.Forms.Timer
System.Threading.Timer
・Threadingの名前空間にあるようにハンドラは(ほぼ)別スレッドで実行されるSystem.Threading.Timer
System.Timers.Timer
・サーバータイマーとも呼ばれる。一番正確とか一番重いとか言われるが、実測するとそうでもない感じ・SynchronizingObjectを指定すればハンドラがUIスレッドで実行される。指定ないと別スレッド
System.Timers.Timer
System.Windows.Threading.DispatcherTimer
・WPFで使用されることが想定される・ハンドラはUIスレッドで実行される
System.Windows.Threading.DispatcherTimer
System.Web.UI.Timer
.NET Coreではつかえません
System.Web.UI.Timer(.net 4.8)
サンプルコード
・WinFormsのプロジェクトで画面にボタンを並べて作成したもののコードビハインド側・DispatcherTimerはWPFプロジェクトで試行したものを合わせて記載してます
using System;
using System.Diagnostics;
using System.Threading;
using System.Windows.Forms;
using System.Windows.Threading;
namespace WindowsFormsApplication1
{
public partial class Form1 : Form
{
public Form1()
{
InitializeComponent();
Debug.WriteLine("Current UI Thread {0}", Thread.CurrentThread.ManagedThreadId);
// Form
var formTimer = new System.Windows.Forms.Timer();
formTimer.Interval = 1000;
formTimer.Tick += (_, __) => Debug.WriteLine("Forms.Timer {0}", Thread.CurrentThread.ManagedThreadId);
buttonFormStart.Click += (_, __) => formTimer.Start();
buttonFormStop.Click += (_, __) => formTimer.Stop();
// Thead
var theadTimer = new System.Threading.Timer((_) => Debug.WriteLine("Threading.Timer {0}", Thread.CurrentThread.ManagedThreadId));
buttonThreadingStart.Click += (_, __) => theadTimer.Change(0, 1000);
buttonThreadingStop.Click += (_, __) => theadTimer.Change(Timeout.Infinite, Timeout.Infinite);
// Server
var serverTimer = new System.Timers.Timer();
serverTimer.Interval = 1000;
serverTimer.Elapsed += (_, __) => Debug.WriteLine("Timers.Timer {0}", Thread.CurrentThread.ManagedThreadId);
buttonServerStart.Click += (_, __) => serverTimer.Start();
buttonServerStop.Click += (_, __) => serverTimer.Stop();
// Server(Sync)
var serverTimer_Sync = new System.Timers.Timer();
serverTimer_Sync.Interval = 1000;
serverTimer_Sync.SynchronizingObject = this; // この設定があるとUIスレッドで実行される
serverTimer_Sync.Elapsed += (_, __) => Debug.WriteLine("Timers.Timer (Sync) {0}", Thread.CurrentThread.ManagedThreadId);
buttonServerStartSync.Click += (_, __) => serverTimer_Sync.Start();
buttonServerStopSync.Click += (_, __) => serverTimer_Sync.Stop();
// Dispatcher
var dispatcherTimer = new DispatcherTimer();
dispatcherTimer.Interval = new TimeSpan(0, 0, 1);
dispatcherTimer.Tick += (_, __) => Debug.WriteLine("Dispatcher.Timer {0}", Thread.CurrentThread.ManagedThreadId);
buttonDispatcherStart.Click += (_, __) => dispatcherTimer.Start();
buttonDispatcherStop.Click += (_, __) => dispatcherTimer.Stop();
}
}
}
どのタイマーを使うのか
WinFormsはFormのタイマー、WPFはDiscatherTimerでよいでしょう。.NET FrameworkならWinFormsでもWindowsBase.dllへ参照を張ってDiscatherTimerを使う手もあります。
サーバーは基本System.Timers.Timerを使うが、場合に寄りSystem.Threading.Timerでしょうか。
使い分けに関しては以下のページが参考になります
Comparing the Timer Classes in the .NET Framework Class Library