あなたが言うように、コールバックはスレッドプールから実行されるため、タイマーとの競合状態は避けられません。ただし、タイマーがまだイベントを実行している間でも、タイマーを安全に破棄できると思います。タイマーイベントの実行がいつ終了したかを知る方法が必要な場合は、Timer.Dispose System.Threading.Timer
( WaitHandle)System.Timers.Timer
を呼び出すことができます。これにより、他のリソース (イベント コンシューマー関数が使用しようとするリソース) も破棄する必要がある場合の競合状態を防ぐことができます。
「即時」の要件に関しては、おそらく、実行を停止するために一種の同期プリミティブを使用するものが最も即時的です。たとえば、次のように考えてください。
static System.Timers.Timer timer;
static void Main(string[] args)
{
var cancelSource = new CancellationTokenSource();
timer = new System.Timers.Timer(200);
timer.Elapsed += new SomeTimerConsumer(cancelSource.Token).timer_Elapsed;
timer.Start();
// Let it run for a while
Thread.Sleep(5000);
// Stop "immediately"
cancelSource.Cancel(); // Tell running events to finish ASAP
lock (timer)
timer.Dispose();
}
class SomeTimerConsumer
{
private CancellationToken cancelTimer;
public SomeTimerConsumer(CancellationToken cancelTimer)
{
this.cancelTimer = cancelTimer;
}
public void timer_Elapsed(object sender, System.Timers.ElapsedEventArgs e)
{
lock (timer)
{
// Do some potentially long operation, that respects cancellation requests
if (cancelTimer.IsCancellationRequested)
return;
// More stuff here
}
}
}
これはおもちゃの例ですが、私の要点を示しています。「すぐに停止」を行う 3 行には、次の機能があります。
Dispose
呼び出しが戻るまでに、どのコード// More stuff here
も再び実行されることはありません。
// More stuff here
ロックのため、タイマーが破棄されている間はコードを実行できません。
- 前の 2 つの機能はロックを必要としますが、タイマーが「すぐに」停止するのを防ぎます。これは、ロックに入ると、すべてのタイマー イベント呼び出しが開始されている場合は終了するまで待機する必要があるためです。このため、タイマーの破棄中にイベントが実行されないことを保証しながら、現在実行中のイベントを中止する最速の方法としてキャンセルを追加しました。
注: 複数のタイマー イベントを同時に実行する必要がある場合ReaderWriterLockSlim
は、モニターの代わりに を使用することを検討してください。