1

特定の間隔でいくつかの操作を実行する必要があります (たとえば、ループで 5 分から 5 分) が、(ボタンを押すと) いつでも完全に停止できる必要があります。

Timerクラスを使用することを考えていましたが、タイマーが停止した後でもイベントが発生する可能性があります。

タイマーでコードを実行しながら、すぐにすべてを完全に停止するにはどうすればよいですか?

正しく理解されているように: 完全な停止とは、イベントが停止し、タイマー自体などのオブジェクトを破棄できることを意味します。タイマーが停止した後に発生する予期しないイベントによる副作用を回避する方法を質問しているわけではありません!

4

3 に答える 3

3

この質問への回答は、操作の種類によって大きく異なります。

最良のシナリオは、ループを使用してスレッドを実行し、中止イベントをリッスンすることです。

static AutoResetEvent abort = new AutoResetEvent();
Thread worker = new Thread(WorkerThread);

void MainThread()
{
  worker.Start();
  Thread.Sleep(30000);
  abort.Set();
}    

void WorkerThread()
{
  while(true)
  {
     if(abort.WaitOne(5000)) return;
     // DO YOUR JOB
  }
}   

abort.Set()別のスレッドから呼び出すと、このスレッドは終了します。

ただし、コードが長時間実行されている場合は、ジョブが完了するまで終了できません。すぐに終了するには、スレッドを中止する必要がありますが、リソースを消費するため、これはあまり賢明ではありません。

または、操作が長時間実行されている場合(たとえば、長い配列を実行している場合)、次のように「中止」イベントの状態を時々確認できます(たとえば、ループのすべての反復)abort.WaitOne(0)

于 2012-10-31T18:28:51.393 に答える
1

あなたが言うように、コールバックはスレッドプールから実行されるため、タイマーとの競合状態は避けられません。ただし、タイマーがまだイベントを実行している間でも、タイマーを安全に破棄できると思います。タイマーイベントの実行がいつ終了したかを知る方法が必要な場合は、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は、モニターの代わりに を使用することを検討してください。

于 2012-10-31T19:27:36.250 に答える
0

次の 2 つのオプションのいずれかを検討します。

  1. 実行する必要があるイベントに安全チェックを入れます。データベースフラグのようなもの。そのため、タイマーが停止に失敗した場合でも、安全チェックが失敗すると、イベントは救済されます。

  2. スケジューリングにはQuartz.Netなどを使用します。これは本当に手間がかかりますが、あなたが望むことをします。

于 2012-10-31T18:23:05.223 に答える