System.Timers.Timerは、それを作成したスレッドとは別のスレッドで経過しますか?
5秒ごとに起動するタイマーを備えたクラスがあるとします。タイマーが作動すると、elapsedメソッドで、一部のオブジェクトが変更されます。このオブジェクトを変更するのに10秒のように長い時間がかかるとしましょう。このシナリオでスレッドの衝突が発生する可能性はありますか?
System.Timers.Timerは、それを作成したスレッドとは別のスレッドで経過しますか?
5秒ごとに起動するタイマーを備えたクラスがあるとします。タイマーが作動すると、elapsedメソッドで、一部のオブジェクトが変更されます。このオブジェクトを変更するのに10秒のように長い時間がかかるとしましょう。このシナリオでスレッドの衝突が発生する可能性はありますか?
回答:
以下の場合でSystem.Timers.Timer:
以下のブライアン・ギデオンの答えを見てください
以下のためのSystem.Threading.Timer:
タイマーに関するMSDNドキュメントは次のように述べています。
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
背後で使用し、必要に応じてタイマーコールバックを受け取った後でマーシャリング操作を実行します。
System.Threading.Timer
またはを使用する必要がありますかSystem.Timers.Timer
?
System.Timers.Timer
2つの操作モードがあります。ランダムに割り当てられたスレッドプールスレッドで実行することも、ISynchronizeInvoke
インスタンスをホストしているスレッドで実行することもできます。それをもっとはっきりさせる方法がわかりません。System.Threading.Timer
元の質問とは(もしあれば)ほとんど関係ありません。
lock
?
前の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秒かかる場合、一部の編集をスキップするにはロックが必要です。
timer.Stop()
Elapsedイベントメソッドの最初にa を追加し、次にElapsedイベントメソッドtimer.Start()
の最後にa を追加すると、Elapsedイベントが衝突しなくなります。
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の前の実行が行われない場合、.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();
}
}