2017-06-24 12 views
1

私の質問に似ているので、私の問題は解決していません。私は数秒ごとにRedisデータベースをポーリングし、結果に基づいてアクションを実行するWindowsサービスを作成しています。私は別のコマンドが別のスレッドで処理されている間にRedisから結果を取得した場合、複数のアクションを同時に実行できるように、ソートの「スレッドプール」を作成したいと思います。Windowsサービスのスレッドを使用して無限にループする

私の主な問題の1つは、私のWindowsサービスを停止すると、プロセスが閉じずに30秒程度の間そのままになってしまうことです。ここでは、関連するコードスニペットは、次のとおりです。

Thread Worker; 
IDatabase db = ...; 
AutoResetEvent StopRequest = new AutoResetEvent(false); 

protected override void OnStart(string[] args) { 
    var poller = new Poller(); 
    Worker = new Thread(() => poller.Poll(StopRequest)); 
    Worker.Start(); 
} 
protected override void OnStop() { 
    // Signal worker to stop and wait until it does 
    StopRequest.Set(); 
    Worker.Join(); 
} 

はここPollerクラスPoll方法の一例です。

public async void Poll(AutoResetEvent finished) 
{ 
    var res = string.Empty; 

    while (!finished.WaitOne(1000)) 
    { 
     res = db.StringGet($"task"); 
     if (!String.IsNullOrEmpty(res)) 
     { 
      ParseAction(res); 
     } 

     db.KeyDelete($"task"); 
    } 
} 

ので(うちトリミング多い)このコードは正常にバックグラウンドで実行されているままで、うまくRedisのから入ってくるクエリを処理するようだが、私は、私として正しく閉じていないプロセスで問題を抱えています上記の通り。私はこれがこのような状況のために取る最良のアプローチであるかどうかも分かりません。私はこのスレッドの問題を処理するためのより良い、またはより "慣用的な"方法についてのいくつかの指針が好きです。

ありがとうございます!

答えて

1

Windowsサービスを処理するより良い方法は、処理全体をバックグラウンドタスクに移行することです。これにより、起動とシャットダウンをはるかに上手く処理することができます。

タスクを使用してポーリングをシミュレートすると、CancellationTokenを使用してシャットダウン・イベントを他の処理層に伝播できます。ここでは、タスクを使用してタイマーをシミュレートする方法を見つけることができます。お読みください Is there a Task based replacement for System.Threading.Timer?

WindowsサービスのOnStartハンドラとOnStopハンドラのコード例を示します。バックグラウンドタスクは、すぐに起動してシャットダウンします。このコードは.NET 4.6.1に基づいています。

using System; 
using System.Collections.Generic; 
using System.Configuration; 
using System.Reflection; 
using System.Threading; 
using System.Threading.Tasks; 
using System.ServiceProcess; 

namespace TEST.MY.SERVICE 
{ 
    partial class MyService : ServiceBase 
    { 
     private Task _initializationTask; 
     private CancellationTokenSource _initializationCancelTokenSource; 
     private CancellationToken _intitializationCancellationToken; 

     public MyService() 
     { 
      InitializeComponent(); 
     } 

     protected override void OnStart(string[] args) 
     { 
     _initializationCancelTokenSource = new CancellationTokenSource(); 
     _intitializationCancellationToken = _initializationCancelTokenSource.Token; 
     _initializationTask = Task.Run(() => 
     { 
      //Kick off polling from here that also uses _intitializationCancellationToken, so that when _initializationCancelTokenSource.Cancel() is invoked from OnStop it will start cancellation chain reaction to stop all running activity. You can pass it even into your methods and check _intitializationCancellationToken.IsCancellationRequested and take appropriate actions. 

       //using the Task timer from the other stack overflow post, You could do something like 
       Task perdiodicTask = PeriodicTaskFactory.Start(() => 
       { 
        Console.WriteLine(DateTime.Now); 
        //execute your logic here that has to run periodically 
       }, intervalInMilliseconds: 5000, // fire every 5 seconds... 
        cancelToken: _intitializationCancellationToken); // Using same cancellation token to manage timer cancellation 

       perdiodicTask.ContinueWith(_ => 
       { 
        Console.WriteLine("Finished!"); 
       }).Wait(); 

     }, _intitializationCancellationToken) 
     .ContinueWith(t => 
     { 
      //deal with any task related errors 
     },TaskContinuationOptions.OnlyOnFaulted); 
     } 

     protected override void OnStop() 
     { 
     try 
     { 
      _initializationCancelTokenSource?.Cancel(); 
      _initializationCancelTokenSource?.Dispose(); 
      _initializationTask?.Dispose(); 
      } 
      catch (Exception stopException) 
      { 
        //log any errors 
      } 
     } 
    } 
} 

ここでは、待機中のタスクをキャンセルする方法の詳細があります。 https://msdn.microsoft.com/en-us/library/dd321315(v=vs.110).aspx

これは、Windowsサービスの設計方法に関する良いアイデアを提供するはずです。あなたのニーズに合わせて必要なものを作りましょう。 c#タスクライブラリに慣れてください。

-1

ブール/バイナリフラグを使用して、サービスが実際に実行されているかどうかを確認しましたか?またはループの開始時にコールを実行してチェックしている可能性がありますか?私はC#で十分に精通していないので、作業全体を完全に理解することができますが、マルチスレッドが関与している場合、バイナリ/ブールフラグは平均的に安定しています。

例として、私はC#を使用しているBeta(Space Engineers)のSteam Gameをプレイしていますが、実行ごとにマルチスレッドエラーや親データのクリアに問題があるようですが、Steam Workshopブールとバイナリフラグを使用する傾向があり、ゲームを再起動するロードタイムが恐ろしいため、タスクがスタックしたりクラッシュしたりしないようにするため、可能な限り多くのクラッシュを回避しようとします。

恐ろしいことではないかもしれませんが、暴走的なメモリリークを引き起こさない限り、正常であるはずです。私は、各タスクの一意識別子にインクリメンタル変数を使用し、どこかに明示的に上限を設定すると、その限度に達すると、タスクを呼び出し、インクリメンタル変数を0にリセットすることをお勧めします。偶発的なデータ損失を防ぐ)。

タスクが実行されている場合、コールを実行し、ブール値を設定して実行します。タスクなしでタスクを実行する前に、タスクがまだ実行中であることを確認する別の呼び出しが必要です情報は何もしません。タスクが実行されていない場合は、if != isRunningが検索され、スレッドを強制終了するための正しい宛先に送信されます。

私は前にも触れたように、この情報があなたに役立つことを願っています。私はC#の初心者ですから、ここでは他のユーザーの一部として高度なコマンドに精通していません。

関連する問題