いろいろ備忘録日記

主に .NET とか Go とか Flutter とか Python絡みのメモを公開しています。

.NET クラスライブラリ探訪-033 (System.Timers.Timer)(サーバータイマ, SynchronizingObject)

.NET Frameworkには、タイマと呼ばれるものが3つあります。

  1. System.Timers.Timer
  2. System.Threading.Timer
  3. System.Windows.Forms.Timer


一つ目が、今回対象となるTimerクラスです。このクラスは、コンポーネントとなっています。
一般的に、サーバータイマ(サーバーベースのタイマとも言います)と呼ばれます。
マルチスレッド環境で、ワーカースレッドとともに利用するのが主です。


二つ目は、スレッドタイマと呼ばれるTimerクラスです。
このTimerクラスは、プログラム内部でのみ利用できます。(他のタイマはツールボックスから利用できます。)
最も軽量なタイマで、イベントの代わりにコールバックを利用します。


三つ目は、Windowsタイマと呼ばれます。
UIを持つアプリケーションから利用出来るよう最適化されています。
具体的には、常にイベントがUIスレッドから呼ばれます。


System.Timers.TimerとSystem.Threading.Timerは、ThreadPoolを利用します。
対して、System.Windows.Forms.TimerはUIスレッド固定です。


どのTimerも使い方は、ほとんど変わりません。
利用する箇所で使い分けるのが、一般的です。


で、今回のSystem.Timers.Timerですが、このタイマは上述したように
マルチスレッド処理の中で利用したり、サーバー側の処理にて利用したりするのが一般的です。
また、Windowsアプリケーションでの利用も想定されていますので、使い勝手がいいのです。


以下、サンプルです。

    #region System.Timers.Timerのサンプル
    public class ServerTimerSamples01 : Form, IExecutable
    {
        System.Timers.Timer _timer;
        ListBox _listBox;
        
        public ServerTimerSamples01()
        {
            InitializeComponent();
            SetTimer();
        }
        
        void InitializeComponent()
        {
            SuspendLayout();
            
            Text = "Timer Sample.";
            FormClosing += OnFormClosing;
            
            _listBox = new ListBox();
            _listBox.Dock = DockStyle.Fill;
            
            Controls.Add(_listBox);
            
            ResumeLayout();
        }
        
        void SetTimer()
        {
            _timer = new System.Timers.Timer();
            
            //
            // イベントをハンドル.
            //
            _timer.Elapsed += OnTimerElapsed;
            
            //
            // System.Timers.Timerはサーバータイマの為
            // ThreadPoolにてイベントが発生する。
            //
            // Elapsedイベント内で、UIコントロールにアクセスする必要がある場合
            // そのままだと、別スレッドからコントロールに対してアクセスしてしまう可能性があるので
            // イベント内にて、Control.Invokeするか、以下のようにSynchronizingObjectを
            // 設定して、イベントの呼び出しをマーシャリングするようにする。
            //
            // 純粋にサーバー側でタイマとして利用する場合は何も設定する必要はない.
            //
            _timer.SynchronizingObject = this;
           
            //
            // 繰り返しの設定.
            //
            _timer.Interval  = 1000;
            _timer.AutoReset = true;
            
            //
            // タイマを開始.
            //
            _timer.Enabled = true;            
        }
        
        public void Execute()
        {
            Application.EnableVisualStyles();
            Application.Run(new ServerTimerSamples01());
        }
        
        void OnFormClosing(object sender, FormClosingEventArgs e)
        {
            _timer.Enabled = false;
            _timer.Dispose();
        }
        
        void OnTimerElapsed(object sender, System.Timers.ElapsedEventArgs e)
        {
            _listBox.Items.Add(String.Format("Time:{0}, ThreadID:{1}", e.SignalTime.ToString("HH:mm:ss"), Thread.CurrentThread.ManagedThreadId.ToString()));
        }
    }
    #endregion


上記ソースの、

_timer.SynchronizingObject = this;


の部分をコメントアウトにして、実行すると
今度は、各時刻がリストボックスに表示される際にThreadIDが異なってきます。
(ちなみに、本来このような場合はControl.Invokeするのが定石ですが、サンプルなので割愛してます。)



================================
過去の記事については、以下のページからご参照下さい。

サンプルコードは、以下の場所で公開しています。