System.Timers.Timerは、それを作成したスレッドとは別のスレッドで経過しますか?
5秒ごとに起動するタイマーを持つクラスがあるとしましょう。タイマーが作動すると、経過メソッドで、一部のオブジェクトが変更されます。このオブジェクトを変更するには、10秒など長い時間がかかるとしましょう。このシナリオでスレッドの衝突が発生する可能性はありますか?
System.Timers.Timer :の場合
以下のブライアンギデオンの回答 を参照してください
System.Threading.Timerクラスは、ThreadPoolスレッドでコールバックを行い、イベントモデルをまったく使用しません。
したがって、実際には別のスレッドでタイマーが経過します。
場合によります。 System.Timers.Timer
には2つの動作モードがあります。
SynchronizingObject
がISynchronizeInvoke
インスタンスに設定されている場合、Elapsed
イベントは同期オブジェクトをホストしているスレッドで実行されます。通常、これらのISynchronizeInvoke
インスタンスは、私たちがよく知っている普通のControl
およびForm
インスタンスに他なりません。したがって、その場合、Elapsed
イベントはUIスレッドで呼び出され、System.Windows.Forms.Timer
と同様に動作します。それ以外の場合、実際に使用された特定のISynchronizeInvoke
インスタンスに依存します。
SynchronizingObject
がnullの場合、Elapsed
イベントはThreadPool
スレッドで呼び出され、System.Threading.Timer
と同様に動作します。実際には、舞台裏でSystem.Threading.Timer
を実際に使用し、マーシャリング操作を行いますafter必要に応じてタイマーコールバックを受け取ります。
前の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秒かかる場合、編集をスキップするにはロックが必要です。
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がインスタンス化されたのと同じスレッドで実行されるようにします。 "
経過イベントが間隔よりも長くかかる場合、別のスレッドを作成して経過イベントを発生させます。しかし、これには回避策があります
static void timer_Elapsed(object sender, ElapsedEventArgs e)
{
try
{
timer.Stop();
Thread.Sleep(2000);
Debug.WriteLine(Thread.CurrentThread.ManagedThreadId);
}
finally
{
timer.Start();
}
}