25

私は C# で記述された Windows サービスを持っています。これは、大量のスレッドを作成し、多くのネットワーク接続 (WMI、SNMP、単純な TCP、http) を作成します。サービス MSC スナップインを使用して Windows サービスを停止しようとすると、サービスを停止するための呼び出しは比較的迅速に返されますが、プロセスは約 30 秒間実行され続けます。

主な問題は、停止するのに 30 秒以上かかる理由は何かということです。何を探すことができ、どのように探すのですか?

2 つ目の質問は、プロセスがまだ実行されているにもかかわらず、サービス msc スナップイン (サービス コントローラー) が返される理由です。プロセスが実際に強制終了されたときにのみ返されるようにする方法はありますか?

サービスの OnStop メソッドのコードは次のとおりです。

protected override void OnStop()
{
   //doing some tracing
   //......

   //doing some minor single threaded cleanup here
   //......

   base.OnStop();

   //doing some tracing here
}

スレッドのクリーンアップの回答に応じて編集する

あなたの多くは、すべてのスレッドを追跡してからクリーンアップする必要があると答えました。私はそれが実際的なアプローチだとは思わない。まず、1 つの場所にあるすべてのマネージド スレッドにアクセスできません。このソフトウェアは、さまざまなコンポーネント、プロジェクト、さらにはスレッドを作成している可能性のあるサードパーティの DLL を含む非常に大きなものです。それらすべてを 1 つの場所で追跡したり、すべてのスレッドがチェックするフラグを保持したりする方法はありません (すべてのスレッドにフラグをチェックさせることができたとしても、多くのスレッドがセマフォなどでブロックしています。ブロックしているときは、チェックしないでください。タイムアウトで待機させてから、このグローバル フラグをチェックして再度待機する必要があります)。

IsBackround フラグは興味深いチェック項目です。繰り返しますが、フォアグラウンド スレッドが実行されているかどうかを確認するにはどうすればよいでしょうか。スレッドを作成するコードのすべてのセクションをチェックする必要があります。他の方法はありますか、これを見つけるのに役立つツールかもしれません。

ただし、最終的には、プロセスは停止します。何かを待つ必要があるようにしか思えません。ただし、OnStop メソッドで X 時間待機すると、プロセスが停止するまでに約 30 秒 + X かかります。私が何をしようとしても、プロセスが実際に停止するために OnStop が戻った後、プロセスには約 30 秒 (常に 30 秒ではなく、変化する可能性があります) が必要なようです。

4

7 に答える 7

18

コールバックが返されるとすぐに、サービスを停止するための呼び出しが返されOnStop()ます。あなたが示したことに基づいて、あなたのOnStop()メソッドはあまり機能しません.

サービスを終了させる方法はいくつかあります。

まず、OnStop()メソッドを作り直して、すべてのスレッドを閉じるように通知し、終了する前にそれらが閉じるのを待つことができます。@DSO が提案したように、グローバル bool フラグを使用してこれを行うことができます (必ず としてマークしてくださいvolatile)。通常は ManualResetEvent を使用しますが、どちらでも機能します。スレッドに終了するように通知します。次に、ある種のタイムアウト期間 (私は通常 3000 ミリ秒を使用します) でスレッドに参加します。それまでにスレッドがまだ終了していない場合は、Abort()メソッドを呼び出してスレッドを終了できます。通常、Abort()メソッドは眉をひそめますが、プロセスがとにかく終了していることを考えると、それは大したことではありません。中断する必要のあるスレッドが一貫してある場合は、そのスレッドを作り直して、シャットダウン シグナルに対する応答性を高めることができます。

次に、スレッドをバックグラウンドスレッドとしてマークします (詳細については、こちらを参照してください)。デフォルトではフォアグラウンドスレッドであるスレッドに System.Threading.Thread クラスを使用しているようです。これを行うと、スレッドがプロセスの終了を妨げないようにします。マネージ コードのみを実行している場合、これは正常に機能します。アンマネージ コードを待機しているスレッドがある場合、 IsBackground プロパティを設定しても、シャットダウン時にスレッドが自動的に終了するかどうかはわかりません。つまり、スレッド モデルを作り直して、このスレッドがシャットダウン要求。

于 2009-10-07T14:25:16.693 に答える
11

OnStop から戻ると、サービス コントロール マネージャー (SCM) が戻ります。したがって、すべてのスレッドが終了するまでブロックするように OnStop 実装を修正する必要があります。

一般的なアプローチは、OnStop ですべてのスレッドを停止するように通知し、それらが停止するのを待つことです。無期限にブロックされるのを避けるために、スレッドに停止する時間制限を与え、時間がかかりすぎる場合はスレッドを中止することができます。

これが私が過去にやったことです:

  1. Stop という名前のグローバル bool フラグを作成し、サービスの開始時に false に設定します。
  2. OnStop メソッドが呼び出されたら、Stop フラグを true に設定し、すべての未処理のワーカー スレッドで Thread.Join を実行します。
  3. 各ワーカー スレッドは Stop フラグをチェックし、それが true の場合は正常に終了します。このチェックは、頻繁に実行する必要があり、長時間実行される操作の前には必ず実行して、サービスのシャットダウンが長時間遅れないようにする必要があります。
  4. OnStop メソッドでは、Join 呼び出しにもタイムアウトを設定して、スレッドが正常に終了するまでの限られた時間を与えます。その後、スレッドを中止します。

#4では、通常、スレッドが終了するのに十分な時間を与える必要があることに注意してください。中止は、スレッドがハングする異常な場合にのみ発生する必要があります...その場合、中止を行うことは、ユーザーまたはシステムがプロセスを強制終了する場合よりも悪くありません (後者は、コンピューターがシャットダウンしている場合)。

于 2009-10-06T21:44:39.343 に答える
1


これを行う簡単な 方法は次のようになります:

ManualResetEvent shutdownEvent;

- サービスの開始時に手動リセット イベントを作成し、初期状態の unsignaled に設定します。

shutdownEvent = new ManualResetEvent(false);

・サービス停止イベント時

shutdownEvent.Set();

スレッドの終了を待つことを忘れないでください

行う
{
 // Service Manager にメッセージを送信して、さらに時間を取得します
 //スレッドが停止するまでの待機時間を制御します
}
while ( not_all_threads_stopped );

-各スレッドは、イベントを停止するために時々テストする必要があります

if ( shutdownEvent.WaitOne(delay, true) ) ブレーク;
于 2009-10-07T15:59:53.383 に答える
0

スレッドループの終了を通知し、クリーンにしてスレッド結合を実行します。問題が発生している場所を測定/ストップウォッチとして実行するのにかかる時間を探します。さまざまな理由で、中途半端なシャットダウンを避けてください。

于 2009-10-06T21:54:01.230 に答える
0

最初の質問に答えるには(サービスが30秒以上実行され続けるのはなぜですか):多くの理由があります。たとえば、WCFを使用している場合、ホストを停止すると、プロセスは着信要求の受け入れを停止し、現在のすべての要求の処理を待機してから停止します。

同じことが他のタイプのネットワーク操作にも当てはまります。操作は終了する前に完了しようとします。これが、ほとんどのネットワークリクエストに、リクエストが「ハング」した場合(サーバーのダウン、ネットワークの問題など)のタイムアウト値が組み込まれている理由です。

正確に何をしているのかについての詳細がなければ、30秒かかる理由を具体的に伝える方法はありませんが、おそらくタイムアウトです。

2番目の質問に答えるには(サービスコントローラーが戻る理由):わかりません。ServiceControllerクラスには、指定された状態に達するまで待機できるWaitForStateメソッドがあることを知っています。サービスコントローラが所定の時間(別のタイムアウト)待機してから、アプリケーションを強制的に終了する可能性があります。

また、base.OnStopメソッドが呼び出され、OnStopメソッドが戻って、プロセスが停止したことをServiceControllerに通知している可能性もありますが、実際には停止していないスレッドがいくつかあります。あなたはこれらのスレッドを終了する責任があります。

于 2009-10-06T21:58:44.537 に答える
0

マット・デイビスはかなり完成度が高いです。
いくつかのポイント; 無限に実行されるスレッド (ほぼ無限ループとすべてをキャッチするため) があり、 service の仕事がそのスレッドを実行することである場合、おそらくそれをフォアグラウンド スレッドにしたいと思うでしょう。

また、タスクのいずれかが sproc 呼び出しなどのより長い操作を実行しているため、結合タイムアウトをもう少し長くする必要がある場合は、実際に SCM にシャットダウンするためにより多くの時間を要求できます。参照: https://msdn.microsoft.com/en-us/library/system.serviceprocess.servicebase.requestadditionaltime(v=vs.110).aspx これは、恐ろしい「削除のマーク」ステータスを回避するのに役立ちます。最大値はレジストリに設定されているため、通常、スレッドが通常シャットダウンする最大予想時間を要求します (12 秒を超えることはありません)。参照:停止要求を処理するためにウィンドウ サービスが待機する最大時間と、追加の時間を要求する方法

私のコードは次のようになります。

private Thread _worker;       
private readonly CancellationTokenSource _cts = new CancellationTokenSource(); 

protected override void OnStart(string[] args)
{
    _worker = new Thread(() => ProcessBatch(_cts.Token));
    _worker.Start();             
}

protected override void OnStop()
{            
    RequestAdditionalTime(4000);
    _cts.Cancel();            
    if(_worker != null && _worker.IsAlive)
        if(!_worker.Join(3000))
            _worker.Abort(); 
}

private void ProcessBatch(CancellationToken cancelToken)
{
   while (true)
   {
       try
       {
           if(cancelToken.IsCancellationRequested)
                return;               
           // Do work
           if(cancelToken.IsCancellationRequested)
                return;
           // Do more work
           if(cancelToken.IsCancellationRequested)
                return;
           // Do even more work
       }
       catch(Exception ex)
       {
           // Log it
       }
   }
}
于 2015-09-29T15:37:50.977 に答える
0

私のように、終了時間を短縮する解決策を探している人は、ServiceHost の CloseTimeout を設定してみてください。

今、それなしで停止するのに時間がかかる理由を理解しようとしています。また、スレッドの問題だと思います。Visual Studio を調べて、サービスにアタッチして停止しました。サービスによって起動されたスレッドがまだ実行されています。

ここで問題となるのは、これらのスレッドが私のサービスを非常にゆっくりと停止させているのだろうかということです。マイクロソフトはそれについて考えていませんでしたか? ポートリリースの問題か、それ以外の問題だと思いませんか? スレッドを処理するのは時間の無駄であり、最終的に閉じる時間が短くならないためです。

于 2014-03-07T09:35:27.403 に答える