10


2

私はキューからタスクを処理する複数のワーカースレッドでプロデューサー/コンシューマーキューモデルを使用するWindowsサービスを持っています。 これらのタスクは、数時間ではないにしても数分程度で非常に長く実行される可能性があり、_ループを含まない_ことがあります。

私の質問は、サービス停止を処理してこれらのワーカースレッドの処理を適切に終了させるための最良の方法についてです。 私は別の SO質問を読んだことがあります。thread.Abort()は悪いデザインの兆候ですが、サービスのOnStop()メソッドは、サービスが終了するまでに限られた時間しか与えられていないようです。 ThreadAbortExceptionのキャッチ内で十分なクリーンアップを行うことができます(矛盾した状態になる危険はありません)。そのため、ワーカースレッドでthread.Abort()を呼び出すことは問題ないようです。 それは…​ですか? 代替案は何ですか?

7 Answer


6


実際、 `+ Abort +`は避けるべきです。 時間をかけて上品に終了することをお勧めします - それからおそらくタイムアウト後に、おそらくそれらを中止することを検討します - しかし結局のところ、サービス停止は代わりにプロセスを殺すことによって同じことをすることができます。

「フラッシュして終了」と言うシグナルをキューに入れようとします-`+ Close +`メソッドのようにhttps://stackoverflow.com/questions/530211/creating-a-blocking-queuet-in-net/ 530228#530228 [こちら]、ただし完了時に何らかの信号が表示されます。

`+ Abort +`に頼る場合-致命傷を負ったプロセスを検討してください。 それをできるだけ早く殺しなさい。


5


"shutdown"のタスクタイプを作成し、各ワーカースレッドごとに1回、それをプロデューサ/コンシューマキューに挿入します。

次にThread.Joinを(タイムアウト付きで)使用して、シャットダウンが完了したことを確認します。


2


NET 4.0では、 `+ System.Threading.Tasks `名前空間を利用して ` Task `オブジェクトを利用できます。 一言で言えば、タスクのキャンセル/中止をより優雅に処理するために、 ` CancellationToken +`を割り当てることができます。

MSDNの詳細については、http://msdn.microsoft.com/en-us/library/system.threading.tasks.aspx [こちら]を参照してください。


1


修正された質問は、実際にはスレッド化とは関係がなく、長時間実行されるアクションを停止する方法と関係があります。 個人的には、大容量ファイル転送などの長時間のストリーム通信活動にAPMを常に使用します。 各コールバックはIO完了プールスレッド上で実行され、迅速に完了し、適度なチャンクを処理して次のパスをスケジュールします。 保留中の操作は、ソケットオブジェクトで `+ Close()+`を呼び出すだけでキャンセルできます。 これは、DIYのスレッド管理よりもはるかに安価で効率的です。

'' '' '

すでに述べたように、 `+ Abort()+`は悪いカルマであり、避けるべきです。

以下の内容は、ループの問題を質問から除外する前に書かれています。

長時間実行されているプロセスがループする場合、それらはループ条件に終了フラグをすべて含める必要があります。そうすれば、終了を知らせることができます。

bool _run; //member of service class

//in OnStart
_run = true;

//in a method on some other thread
while ((yourLoopCondition) & _run)
{
  //do stuff
  foreach (thing in things)
  {
    //do more stuff
    if (!_run) break;
  }
}
if (!_run) CleanUp();

//in OnStop
_run = false;

厳密には揮発性物質を使用するべきですが、制御ロジックだけがフラグを設定するので重要ではありません。 技術的には競合状態が存在しますが、それはちょうどあなたがもう一回巡回するかもしれないことを意味します。


0


ManualResetEventを使用して、イベントがシグナル化されているかどうかを確認します。 スレッド同期(C#プログラミングガイド)のサンプルを参照してください。


0


これは、Windowsサービスでスレッドを停止するために使用するコードです(スレッドプールを使用せずに、直接スレッドを使用してください)。

// signal all threads to stop
this.AllThreadsStopSignal.Set();

if(logThreads.IsDebugEnabled)logThreads.Debug( "ワーカーを停止");

// remember the time of the signal
DateTime signalTime = DateTime.Now;

// create an array of workers to be stopped
リストworkersToBeStopped = newリスト(ワーカー)。

while(true){//しばらくお待ちください。Thread.Sleep(1000);

//リストを調べて、作業者が作業を停止したかどうかを確認するint i = 0; while(i <workersToBeStopped.Count){IServiceWorker workerToBeStopped = workersToBeStopped [i];

if(log.IsDebugEnabled)log.Debug(String.Format(System.Globalization.CultureInfo.InvariantCulture)、 "ワーカー '{0}'を停止しています。 ワーカー状態= {1} "、workerToBeStopped.WorkerDescription、workerToBeStopped.WorkerState));

bool stopped = workerToBeStopped.JoinThread(TimeSpan.Zero);

//停止した場合は、リストから削除します。if(stopped){workersToBeStopped.RemoveAt(i);} if(log.IsDebugEnabled)log.Debug(String.Format(System.Globalization.CultureInfo.InvariantCulture、 "Worker '{0}'が停止されました。"、workerToBeStopped.WorkerDescription)); }その他{i; if(log.IsDebugEnabled)log.Debug(String.Format(System.Globalization.CultureInfo.InvariantCulture) "Worker '{0}'を停止できませんでした。後でもう一度やり直してください。 ワーカー状態= {1} "、workerToBeStopped.WorkerDescription、workerToBeStopped.WorkerState));}}

//すべてのワーカーが停止した場合、(workersToBeStopped.Count == 0)がブレークした場合はループを終了します。

//停止時間が最大時間を超えていないか確認します。DateTime nowTime = DateTime.Now; TimeSpan duration = nowTime  -  signalTime。

if(duration> serviceCustomization.ThreadTerminationMaxDuration){// foreachを停止していないすべてのワーカーの強制終了を実行します(workersToBeStoppedのIServiceWorkerワーカー){try {log.Warn(String.Format(System.Globalization.CultureInfo.InvariantCulture、) '{0}。 "、worker.WorkerDescription)); worker.Abort(); log.Warn(String.Format(System.Globalization.CultureInfo.InvariantCulture、 "Worker '{0}'は中止されました。"、worker.WorkerDescription)); } catch(ThreadStateException ex){log.Warn(String.Format(System.Globalization.CultureInfo.InvariantCulture、 "Worker '{0}'は中止できませんでした。"、worker.WorkerDescription); ex);壊れます。 }}


0


あなたのプロセスがとにかくシャットダウンされているなら、私は個人的にAbort()を使うことによる問題を見ません。 私は別の方法を見つけようとするでしょう、しかし結局、あなたがとにかくメインスレッドでクリーンアップをしているならば*それは問題ではありません。

別の選択肢は、ワーカースレッドを backgroundスレッドとしてマークすることです。 そうすれば、プロセスが閉じると自動的に閉じます。 終了する前にAppDomain.ProcessExitイベントを使用してクリーンアップすることができます。