5

Windowsサービスにスレッド(STAThread)があり、大量の作業を実行します。Windowsサービスが再起動されたら、このスレッドを正常に停止したいと思います。

私はいくつかの方法を知っています

  • 揮発性ブール
  • ManualResetEvent
  • CancellationToken

私がThread.Abortを見つけた限り、それはダメです...

ベストプラクティスは何ですか?作業はスレッドが開始されたクラスとは別のクラスで実行されるため、コンストラクターにcancellationTokenパラメーターを導入するか、たとえば揮発性変数を使用する必要があります。しかし、私は何が最も賢いのか理解できません。

更新
少し明確にするために、私が話していることの非常に簡単な例をまとめました。前に述べたように、これはWindowsサービスで実行されています。現在、ループまたはcancellationTokenでチェックされる揮発性ブール値を考えています。ループが終了するのを待つことはできません。以下に説明するように、数分かかる場合があり、サーバーのシステム管理者は次のように信じています。サービスを再起動する必要があるときに、サービスに問題があります。問題なくループ内のすべての作業を問題なくドロップできますが、スレッドではこれを実行できません。中止すると、「悪」であり、さらにCOMです。インターフェイスが呼び出されるため、小さなクリーンアップが必要です。

Class Scheduler{
  private Thread apartmentThread;
  private Worker worker;

  void Scheduling(){
    worker = new Worker();
    apartmentThread = new Thread(Run);
    apartmentThread.SetApartmentState(ApartmentState.STA);
    apartmentThread.Start();    
  }

  private void Run() {
    while (!token.IsCancellationRequested) {
      Thread.Sleep(pollInterval * MillisecondsToSeconds);
      if (!token.IsCancellationRequested) {
        worker.DoWork();
      }
    }
  }
}

Class Worker{
  //This will take several minutes....
  public void DoWork(){
    for(int i = 0; i < 50000; i++){
      //Do some work including communication with a COM interface
      //Communication with COM interface doesn't take long
    }
  }
}

UPDATE
コードでisCancelled状態が「調べられた」cancellationTokenを使用してパフォーマンスを調べたところ、ManualResetEventSlimでwaitOneを使用するよりもはるかに高速です。いくつかの簡単な数字、forループで100.000.000回反復するcancellationTokenの場合は約 500ミリ秒、WaitOneの費用は約。3秒。したがって、このシナリオでのパフォーマンスは、cancellationTokenを使用する方が高速です。

4

4 に答える 4

4

私はboolフラグ、lockオブジェクト、および次のようなTerminate()メソッドを使用する傾向があります。

object locker = new object();
bool do_term = false;

Thread thread = new Thread(ThreadStart(ThreadProc));
thread.Start();

void ThreadProc()
{
    while (true) {
        lock (locker) {
            if (do_term) break;
        }

        ... do work...
    }
}

void Terminate()
{
    lock (locker) {
        do_term = true;
    }
}

Terminate()の他に、他のすべてのフィールドとメソッドは「worker」クラス専用です。

于 2012-11-20T15:40:42.783 に答える
4

あなたはあなたの実装を十分に投稿していませんが、それがCancellationTokenあなたに利用可能であるならば、私は強くお勧めします。保守性の観点から使用して理解するのは簡単です。複数のワーカースレッドを使用する場合は、協調キャンセルも設定できます。

このスレッドが長期間ブロックされる可能性がある状況に陥った場合は、これが発生しないようにアーキテクチャを設定することをお勧めします。停止するように指示したときにうまく再生されないスレッドを開始するべきではありません。あなたが彼らに尋ねたときに彼らが止まらない場合、唯一の本当の方法はプロセスを解体し、OSに彼らを殺させることです。

Eric Lippertは、やや関連性のある質問に対する素晴らしい回答をここに投稿しました。

于 2012-11-20T16:08:06.773 に答える
4

WaitHandle、最も好ましくはManualResetEventを使用します。最善の策は、ループ内にあるものをすべて終了させることです。これはあなたの目標を達成するための最も安全な方法です。

ManualResetEvent _stopSignal = new ManualResetEvent(false); // Your "stopper"
ManualResetEvent _exitedSignal = new ManualResetEvent(false);

void DoProcessing() {
    try {
        while (!_stopSignal.WaitOne(0)) {
            DoSomething();
        }
    }
    finally {
        _exitedSignal.Set();
    }
}

void DoSomething() {
    //Some work goes here
}

public void Terminate() {
    _stopSignal.Set();
    _exitedSignal.WaitOne();
}

次にそれを使用するには:

Thread thread = new Thread(() => { thing.DoProcessing(); });
thread.Start();

//Some time later...
thing.Terminate();

「DoSomething」実装で特に長時間実行されるプロセスがある場合は、それを非同期的に呼び出して、状態情報を提供することをお勧めします。ただし、これはかなり複雑になる可能性があります。プロセスが終了するまで待ってから、可能であれば終了することをお勧めします。

于 2012-11-20T16:16:06.397 に答える
3

スレッドが見つかる可能性のある状況は2つあります。

  • 処理。
  • ブロッキング。

スレッドが何かを処理している場合、スレッドが安全に終了するためには、スレッドが処理を終了するのを待つ必要があります。作業ループの一部である場合は、ブールフラグを使用してループを終了できます。

スレッドがブロックされている場合は、スレッドをスリープ解除して、再度処理する必要があります。スレッドはManualResetEvent、データベース呼び出し、ソケット呼び出し、またはその他のブロックできるものをブロックしている可能性があります。ウェイクアップするには、Thread.Interrupt()を発生させるメソッドを呼び出す必要がありますThreadInterruptedException

次のようになります。

private object sync = new object():
private bool running = false;

private void Run()
{
    running = true;
    while(true)
    {
        try
        {
            lock(sync)
            {
                if(!running)
                {
                    break;
                }
            }

            BlockingFunction();
        }
        catch(ThreadInterruptedException)
        {
            break;
        }
    }
}

public void Stop()
{
    lock(sync)
    {
        running = false;
    }
}

そして、これがあなたがそれを使うことができる方法です:

MyRunner r = new MyRunner();
Thread t = new Thread(()=>
{
    r.Run();
});

t.IsBackground = true;
t.Start();

// To stop the thread
r.Stop();

// Interrupt the thread if it's in a blocking state
t.Interrupt();

// Wait for the thread to exit
t.Join();
于 2012-11-20T16:03:36.783 に答える