2009-07-17 13 views
5

私はアプリケーションライフタイムのために動作する小さなバックグラウンドスレッドを持っていますが、アプリケーションがシャットダウンしてもスレッドは正常に終了します。Thread.Interruptを使用してアプリケーションシャットダウン時に長いスリープを停止する - 良い方法がありますか?

問題は、スレッドが15分の間隔でいくつかのコードを実行することです。つまり、スリープしています。

今すぐスリープから抜け出すために、私はそれに割り込みを投げます - 私の質問は、割り込みがThreadInterruptedExceptionを生成するので、これに対するよりよいアプローチがある場合です。

は、ここに私のコード(やや擬似)の要旨です:

public class BackgroundUpdater : IDisposable 
{ 
    private Thread myThread; 
    private const int intervalTime = 900000; // 15 minutes 
    public void Dispose() 
    { 
     myThread.Interrupt(); 
    } 

    public void Start() 
    { 
     myThread = new Thread(ThreadedWork); 
     myThread.IsBackground = true; // To ensure against app waiting for thread to exit 
     myThread.Priority = ThreadPriority.BelowNormal; 
     myThread.Start(); 
    } 

    private void ThreadedWork() 
    { 
     try 
     { 
      while (true) 
      { 
       Thread.Sleep(900000); // 15 minutes 
       DoWork(); 
      } 
     } 
     catch (ThreadInterruptedException) 
     { 
     } 
    } 
} 

答えて

14

あります絶対に良い方法 - 代わりにスリープ/割り込みのMonitor.Wait/Pulseを使用するか、またはAuto/ManualResetEventを使用しますか。

私は、おそらくJavaのwait()/ notify()メカニズムのような理由から、Wait/Pulseファンです。これはおそらく、ManualResetEventが必要です。しかし、リセットイベントがより有用な時代は間違いありません。詳細については

private readonly object padlock = new object(); 
private volatile bool stopping = false; 

public void Stop() // Could make this Dispose if you want 
{ 
    stopping = true; 
    lock (padlock) 
    { 
     Monitor.Pulse(padlock); 
    } 
} 

private void ThreadedWork() 
{ 
    while (!stopping) 
    { 
     DoWork(); 
     lock (padlock) 
     { 
      Monitor.Wait(padlock, TimeSpan.FromMinutes(15)); 
     } 
    } 
} 

wait handlesに、deadlocks, waiting and pulsingのページ特に、ページを自分のthreading tutorialを参照してください。

あなたのコードは次のようになります。 Joe Albahari also has a tutorialは、同じトピックを扱い、それらを比較します。

Parallel Extensionsにもこれを簡単にする機能があれば、詳細はまだ詳しくはわかりませんが、驚くことはありません。

+0

あなたは速すぎます。私はあきらめます;) – frast

+0

すてきな解決、ありがとう:-) – Steffen

+2

ロックを保持している間に作業スレッドが待機しているので、stop()メソッドのロックが最大15分間ブロックされる作業スレッドはロックを解除しますか? –

0

スレッドがサブスクライブするキャンセルイベントまたはデリゲートを追加する方法があります。 cancelイベントが呼び出されると、スレッドは自動的に停止することができます。

2

あなたは、プロセスは次のように終了すべきかどうかをチェックするには、イベントを使用することができます。

var eventX = new AutoResetEvent(false); 
while (true) 
{ 
    if(eventX.WaitOne(900000, false)) 
    { 
     break; 
    } 
    DoWork(); 
} 
0

私は絶対ジョンSkeetsの答えのよう。バックグラウンドタスクは、実際にこのような場合には(停止しているようにするために、処分以外のスレッドによって呼び出されなければならない待ちを含め

public class BackgroundTask : IDisposable 
{ 
    private readonly CancellationTokenSource cancellationTokenSource; 
    private bool stop; 

    public BackgroundTask() 
    { 
     this.cancellationTokenSource = new CancellationTokenSource(); 
     this.stop = false; 
    } 

    public void Stop() 
    { 
     this.stop = true; 
     this.cancellationTokenSource.Cancel(); 
    } 

    public void Dispose() 
    { 
     this.cancellationTokenSource.Dispose(); 
    } 

    private void ThreadedWork(object state) 
    { 
     using (var syncHandle = new ManualResetEventSlim()) 
     { 
      while (!this.stop) 
      { 
       syncHandle.Wait(TimeSpan.FromMinutes(15), this.cancellationTokenSource.Token); 
       if (!this.cancellationTokenSource.IsCancellationRequested) 
       { 
        // DoWork(); 
       } 
      } 
     } 
    } 
} 

か、を:しかし、この少し理解しやすくしても動作するはずであるかもしれません)それが実際に開始したとワーカースレッドを必要とし、1つのバックグラウンドスレッドが実行されている、そしてもちろん、これは完璧なコードではありません。

using System; 
using System.Threading; 

public class BackgroundTask : IDisposable 
{ 
    private readonly ManualResetEventSlim threadedWorkEndSyncHandle; 
    private readonly CancellationTokenSource cancellationTokenSource; 
    private bool stop; 

    public BackgroundTask() 
    { 
     this.threadedWorkEndSyncHandle = new ManualResetEventSlim(); 
     this.cancellationTokenSource = new CancellationTokenSource(); 
     this.stop = false; 
    } 

    public void Dispose() 
    { 
     this.stop = true; 
     this.cancellationTokenSource.Cancel(); 
     this.threadedWorkEndSyncHandle.Wait(); 
     this.cancellationTokenSource.Dispose(); 
     this.threadedWorkEndSyncHandle.Dispose(); 
    } 

    private void ThreadedWork(object state) 
    { 
     try 
     { 
      using (var syncHandle = new ManualResetEventSlim()) 
      { 
       while (!this.stop) 
       { 
        syncHandle.Wait(TimeSpan.FromMinutes(15), this.cancellationTokenSource.Token); 
        if (!this.cancellationTokenSource.IsCancellationRequested) 
        { 
         // DoWork(); 
        } 
       } 
      } 
     } 
     finally 
     { 
      this.threadedWorkEndSyncHandle.Set(); 
     } 
    } 
} 

あなたは私が好きなジョンSkeetsソリューションの上に任意の欠陥や欠点が表示された場合私はいつも学習を楽しむようにそれらを聞くために;-) 私はこれがsだと思いますより多くのメモリを使用するため、大規模で短期間で使用する必要はありません。他の?

1

.NET 4以降には、このタスクを少し簡略化したCancellationTokenSourceクラスがあります。

private readonly CancellationTokenSource cancellationTokenSource = 
    new CancellationTokenSource(); 

private void Run() 
{ 
    while (!cancellationTokenSource.IsCancellationRequested) 
    { 
     DoWork(); 
     cancellationTokenSource.Token.WaitHandle.WaitOne(
      TimeSpan.FromMinutes(15)); 
    } 
} 

public void Stop() 
{ 
    cancellationTokenSource.Cancel(); 
} 

CancellationTokenSourceは使い捨てであることを忘れないでください。正しく処分してください。