web-dev-qa-db-ja.com

C#タイマーは別のスレッドで経過しますか?

System.Timers.Timerは、それを作成したスレッドとは別のスレッドで経過しますか?

5秒ごとに起動するタイマーを持つクラスがあるとしましょう。タイマーが作動すると、経過メソッドで、一部のオブジェクトが変更されます。このオブジェクトを変更するには、10秒など長い時間がかかるとしましょう。このシナリオでスレッドの衝突が発生する可能性はありますか?

84
user113164

System.Timers.Timerの場合

以下のブライアンギデオンの回答 を参照してください

System.Threading.Timerの場合

タイマーに関するMSDNドキュメント 状態:

System.Threading.Timerクラスは、ThreadPoolスレッドでコールバックを行い、イベントモデルをまったく使用しません。

したがって、実際には別のスレッドでタイマーが経過します。

54
Joren

場合によります。 System.Timers.Timerには2つの動作モードがあります。

SynchronizingObjectISynchronizeInvokeインスタンスに設定されている場合、Elapsedイベントは同期オブジェクトをホストしているスレッドで実行されます。通常、これらのISynchronizeInvokeインスタンスは、私たちがよく知っている普通のControlおよびFormインスタンスに他なりません。したがって、その場合、ElapsedイベントはUIスレッドで呼び出され、System.Windows.Forms.Timerと同様に動作します。それ以外の場合、実際に使用された特定のISynchronizeInvokeインスタンスに依存します。

SynchronizingObjectがnullの場合、ElapsedイベントはThreadPoolスレッドで呼び出され、System.Threading.Timerと同様に動作します。実際には、舞台裏でSystem.Threading.Timerを実際に使用し、マーシャリング操作を行いますafter必要に応じてタイマーコールバックを受け取ります。

180
Brian Gideon

前のElapsedがまだ実行されていない限り、各経過イベントは同じスレッドで発生します。

衝突を処理します

これをコンソールに入れてみてください

static void Main(string[] args)
{
    Debug.WriteLine(Thread.CurrentThread.ManagedThreadId);
    var timer = new Timer(1000);
    timer.Elapsed += timer_Elapsed;
    timer.Start();
    Console.ReadLine();
}

static void timer_Elapsed(object sender, ElapsedEventArgs e)
{
    Thread.Sleep(2000);
    Debug.WriteLine(Thread.CurrentThread.ManagedThreadId);
}

このようなものが得られます

10
6
12
6
12

ここで、10は呼び出しスレッドであり、6と12はbg経過イベントから発生しています。 Thread.Sleep(2000)を削除した場合;このようなものが得られます

10
6
6
6
6

衝突がないため。

しかし、これでもまだ問題が残っています。 uが5秒ごとにイベントを起動し、編集に10秒かかる場合、編集をスキップするにはロックが必要です。

21
Simon

System.Timers.Timerでは、SynchronizingObjectが設定されていない場合、別のスレッドで。

    static System.Timers.Timer DummyTimer = null;

    static void Main(string[] args)
    {
        try
        {

            Console.WriteLine("Main Thread Id: " + System.Threading.Thread.CurrentThread.ManagedThreadId);

            DummyTimer = new System.Timers.Timer(1000 * 5); // 5 sec interval
            DummyTimer.Enabled = true;
            DummyTimer.Elapsed += new System.Timers.ElapsedEventHandler(OnDummyTimerFired);
            DummyTimer.AutoReset = true;

            DummyTimer.Start();

            Console.WriteLine("Hit any key to exit");
            Console.ReadLine();
        }
        catch (Exception Ex)
        {
            Console.WriteLine(Ex.Message);
        }

        return;
    }

    static void OnDummyTimerFired(object Sender, System.Timers.ElapsedEventArgs e)
    {
        Console.WriteLine(System.Threading.Thread.CurrentThread.ManagedThreadId);
        return;
    }

DummyTimerが5秒間隔で起動したかどうかを確認する出力:

Main Thread Id: 9
   12
   12
   12
   12
   12
   ... 

このように、OnDummyTimerFiredはワーカースレッドで実行されます。

いいえ、さらに複雑です-間隔を10ミリ秒に短縮すると、

Main Thread Id: 9
   11
   13
   12
   22
   17
   ... 

これは、次のティックが起動されたときにOnDummyTimerFiredのprev実行が行われない場合、.NETがこのジョブを実行するための新しいスレッドを作成するためです。

さらに複雑なこと、」System.Timers.Timerクラスは、このジレンマに対処する簡単な方法を提供します。これは、パブリックSynchronizingObjectプロパティを公開します。このプロパティをWindowsフォームのインスタンスに設定します(またはWindowsフォーム上のコントロール)は、ElapsedイベントハンドラーのコードがSynchronizingObjectがインスタンス化されたのと同じスレッドで実行されるようにします。 "

http://msdn.Microsoft.com/en-us/magazine/cc164015.aspx#S2

15
Swab.Jat

経過イベントが間隔よりも長くかかる場合、別のスレッドを作成して経過イベントを発生させます。しかし、これには回避策があります

static void timer_Elapsed(object sender, ElapsedEventArgs e)    
{     
   try
   {
      timer.Stop(); 
      Thread.Sleep(2000);        
      Debug.WriteLine(Thread.CurrentThread.ManagedThreadId);    
   }
   finally
   {
     timer.Start();
   }
}
12
Rajan