2009-11-19 8 views
14

私はこのようないくつかのコードを維持しています。それは30分ごとにいくつかの作業を行うWindowsサービスです。 ActualWorkDoneHereメソッドの実行には約30秒かかりますが、実行中に停止していれば、状態が悪い状態になる可能性があります。それを防ぐ最善の方法は何ですか? onstopメソッドでfalseに設定されたブール値でWhile(true)を置き換えるべきです(スレッドAbort呼び出しを削除します)?スレッドがスリープ状態にあるかどうかを確認する方法はありますか?Windowsサービスで実行されているC#.NETスレッドを安全に停止するにはどうすればよいですか?

namespace WorkService 
{ 
    public partial class WorkService : ServiceBase 
    { 
     private Thread _workerThread = null; 

     public WorkService() 
     { 
      InitializeComponent(); 
     } 

     protected override void OnStart(string[] args) 
     { 
      _workerThread = new Thread(new ThreadStart(DoWork)); 
      _workerThread.Start(); 
     } 

     protected override void OnStop() 
     { 
      _workerThread.Abort(); 
     } 

     static void DoWork() 
     { 
      int sleepMinutes = 30; 

      while (true) 
      { 
       ActualWorkDoneHere(); 

       System.Threading.Thread.Sleep(new TimeSpan(0, sleepMinutes, 0)); 
      } 
     } 
    } 
} 
+1

これは、タスクスケジューラのための仕事のように見えるの内側にこれを試してみてください! :) –

+2

@Gregスケジュールでタスクスケジューラを実行しませんか?この場合、Stimyは30分間停止してから再度実行し、30分ごとに実行しないようにしています。 – Junto

答えて

22

このような場合は、通常ManualResetEventを使用します。これはStop()コールで設定されます。それから、私はタイムアウトを待つ:

for (;;) 
{ 
    if (_stop.WaitOne(timeout)) 
     break; 
    DoSomething(); 
} 
+1

+1 DoSomething()をいくつかの点で停止させ、データを不整合な状態にすることができない場合は、 'if(_stop.WaitOne(0)){... return ...}' 30秒まで待つことを避けます。 – Gonzalo

+1

+1そして私はManualResetEventを使って、スレッドが実際に停止したときにシグナルを発します。したがって、stopメソッドはスレッドに停止を指示した後、このシグナルを待機します。 –

+0

スレッドが停止したときに通知するイベントは必要ありません。 Thread.Joinを呼び出すだけです。 –

2

自分で実装するのは唯一の安全なオプションです。スレッドがスリープしているかどうかを調べる方法が見つかったとしても、スレッドを終了しようとすると競合状態になります(チェックした後、そして終了する前に処理が開始される可能性があるため)。

Thread.Sleepの代わりに、 500msをスリープし、アボートフラグがまだfalseであるかどうかを確認し、30分経過する前に別の500msなどをスリープさせてから、ジョブなどを実行します(これは実用的なアプローチになります)。より洗練されたものが必要な場合は、MainResetEventをタイムアウトとともに使用して、メインスレッドがアボートする時間を知らせるのを待つことができます。

0

サービスの停止を処理するために自動リセットフラグを使用してみてください。その場合、スレッド中断を実行する必要はありません。

namespace WorkService 
{ 
    public partial class WorkService : ServiceBase 
    { 
    AutoResetEvent serviceStopEvent = new AutoResetEvent(false); 

     public WorkService() 
     { 
      InitializeComponent(); 
     } 

     protected override void OnStart(string[] args) 
     { 
      Thread workerThread = new Thread(new ThreadStart(DoWork)); 
      workerThread.Start(); 
     } 

     protected override void OnStop() 
     { 
      serviceStopEvent.Set(); 
     } 

     static void DoWork() 
     { 
      int sleepMinutes = 30; 
     WaitHandle[ ] handles = new WaitHandle[ ] { serviceStopEvent }; 

      while (WaitHandle.WaitAny(handles)) 
      { 
       ActualWorkDoneHere(); 

      } 
     } 

    } 
} 

乾杯、 バーラト以下のサンプルコードを追加しました。

+0

-1 AutoResetEventは停止するまで常に設定解除され、ActualWorkDoneHere()は決して実行されません。 – Gonzalo

+0

開始時にリセットイベントを追加するのを忘れました。 –

1

これは1つの方法です。あなたのクラスに次の変数を追加します。

private readonly object syncObject = new object(); 
private bool stopping; 
private bool stopped = true; 

その後のOnStartに、あなたは、この(私はこの例では、いくつかのロギングを行いヘルパーメソッドを持っているし、「ファイル名を指定して実行」の方法は、実際の作業を行います)のような何かを行います。 :

public override void OnStart() 
    { 
     while (stopping) 
     { 
      Thread.Sleep(MSECS_SLEEP_FOR_STOP); 
     } 

     lock (syncObject) 
     { 
      // make sure task isn't already started 
      if (!stopped) 
      { 
       Helper.WriteToLog(logger, Level.INFO, 
        string.Format("{0} {1}", TASK_NAME, "is already started.")); 
       return; 
      } 
      stopped = false; 
     } 

     // start task in new thread 
     Thread thread = new Thread(Run); 
     thread.Start(); 

     Helper.WriteToLog(logger, Level.INFO, 
      string.Format("{0} {1}", TASK_NAME, "was started.")); 
    } 

あなたの「ファイル名を指定して実行」スレッドの作業を行う方法、processIntervalは、あなたが実行の間待ちたいどのくらいだろうあなたは、コンストラクタで設定するか、それをハードコーディングでき(このようになります。 ):

private void Run() 
    { 
     try 
     { 
      while (!stopping) 
      { 
       // do work here 

       // wait for process interval 
       DateTime waitStart = DateTime.Now; 
       while (((DateTime.Now - waitStart).TotalMilliseconds < processInterval) && !stopping) 
       { 
        // give processing time to other threads 
        Thread.Sleep(MSECS_SLEEP_FOR_CHECK); 
       } 
      } 
      lock (syncObject) 
      { 
       stopped = true; 
       stopping = false; 
      } 

      Helper.WriteToLog(logger, Level.INFO, 
       string.Format("{0} {1}", TASK_NAME, "was stopped.")); 
     } 
     catch (Exception e) 
     { 
      // log the exception, but ignore it (i.e. don't throw it) 
      Helper.LogException(logger, MethodBase.GetCurrentMethod(), e); 
     } 
    } 

次にOnStopで、あなたがこれを行うになります。

public override void OnStop() 
    { 
     lock (syncObject) 
     { 
      if (stopping || stopped) 
      { 
       Helper.WriteToLog(logger, Level.INFO, 
        string.Format("{0} {1}", TASK_NAME, "is already stopped.")); 
       return; 
      } 
      stopping = true; 
     } 
    } 
1

あなたはあなたの仕事が実際に起こっている間に停止しているスレッドを防ぐために、ロックオブジェクトを使用することができますが...

private static readonly object _syncRoot = new object(); 

    protected override void OnStop() 
    { 
     lock (_syncRoot) 
     { 
      _workerThread.Abort(); 
     } 
    } 

    static void DoWork() 
    { 
     int sleepMinutes = 30; 

     while (true) 
     { 
      lock (_syncRoot) 
      { 
       ActualWorkDoneHere(); 
      } 

      System.Threading.Thread.Sleep(new TimeSpan(0, sleepMinutes, 0)); 
     } 
    } 

あなたは注意する必要がありますあなたのActualWorkDoneHere()機能が長時間かかる場合、ウィンドウはサービスが停止しないと報告します。

2

誰もがこれをとても複雑にしています。

オンレース: 元のポストは、修正されたOnStopのレースを持っていました。私が知る限り、サービスを停止状態にすることは、タイマーのサービスに使用されるスレッドプールスレッドを中止しません。タイマーの起動と同時に停止しているサービスの条件は無関係です。ActualWorkDoneHere()は実行されるか、実行されません。どちらも許容できる条件です。

namespace WorkService 
{ 
    public partial class WorkService : ServiceBase 
    { 
     protected const int sleepMinutes = 30; 
     protected System.Timers.Timer _interval; 
     protected bool _running = false; 

     public WorkService() 
     { 
      InitializeComponent(); 
      _interval = new System.Timers.Timer(); 
      _interval.Elapsed += new ElapsedEventHandler(OnTimedEvent); 
      _interval.Interval = sleepMinutes * 60 * 1000; 
      _running = false; 
     } 

     protected override void OnStart(string[] args) 
     { 
      _running = true; 
      _interval.Enabled = true; 
     } 

     protected override void OnStop() 
     { 
      _interval.Enabled = false; 
      _running = false; 
     } 

     private static void OnTimedEvent(object source, ElapsedEventArgs e) 
     { 
      if(_running) 
       ActualWorkDoneHere(); 
     } 
    } 
} 
+0

レースコンディションがあります。 – scottm

+0

まだ誰かが競争条件を感じている場合は説明してください。 – JeffreyABecker

+0

@JeffreyABeckerデフォルトでは、タイマーは複数のスレッドを同時に実行することができるため、競合状態の可能性があります。 ManualResetEventは、常に1つのスレッドでデフォルトで実行されます。 –

0

私のサービスはネットワークソケットの接続されたペアを作成し、selectシステムコールを使用して両方をリッスンします。結合されたペアが読み込み準備ができていると報告した場合、私はサービスをシャットダウンすることを知っていた

このトリックを使用して、接続されたペアから実際に読み取られていない限り、シャットダウンする任意の数のスレッドをトリガーできます。

0
while (true) 
     { 
      if (m_reset.WaitOne(1,false)) 
       break; 
      // DoSomething 


     } 

onStop()

関連する問題