2011-06-27 6 views
2

私は、タイマー(System.Threading.Timer)の動的リストを作成し、シグナルが受信されて終了するまでプロセスを継続します。信号が終了するために受信されると、私は、既存のタイマーコールバックが完了(下記参照)にしたい:処理後に複数のThreading.Timersを監視する

private IList<Timer> _timers = new List<Timer>(); 
... 
... 
private void WaitOnExecutingThreads() 
{ 
    var waiters = new List<ManualResetEvent>(_timers.Count); 

    foreach (var timer in _timers) 
    { 
     var onWait = new ManualResetEvent(false); 
     waiters.Add(onWait); 
     timer.Dispose(onWait); 
    } 

    WaitHandle.WaitAll(waiters.ToArray()); 
    waiters.ForEach(x=> x.Dispose()); 
} 

このコードは、今動作しますが、タイマーが配置されたら、私は現在進行中のスレッドのコールバックを監視したいと思います。私の意図は、一定の間隔で「タイマーAがまだ実行中です」というログに書き込むことです。

私は一緒に遊んで開始しました:

ThreadPool.RegisterWaitForSingleObject(....) 

アドオンには、以下を追加しました: (注:私はタイマーと関連するデータが含まれているクラスはThreadContextを作成した)

private void WaitOnExecutingThreads() 
{ 
    var waiters = new List<ManualResetEvent>(); 

    WaitOrTimerCallback IsRunning = (x, timeout) => { if (timeout) { Log(x + "is still running"); } }; 

    foreach (var threadContext in _threadContexts) 
    { 
     var onWait = new ManualResetEvent(false); 
     threadContext.Timer.Dispose(onWait); 

     ThreadPool.RegisterWaitForSingleObject(onWait, IsRunning , threadContext.ThreadInfo.Id, new TimeSpan(0, 0, 30), false); 

     waiters.Add(onWait); 
    } 

     WaitHandle.WaitAll(waiters.ToArray()); 
     waiters.ForEach(x=> x.Dispose()); 
    } 

私はこのように感じるがすべきC#.net 4.0ではまっすぐな前進の仕事になります。私の単純な単体テストでは、待ってからMy IsRunningコールバックがかなり起動します。私はこの呼び出しの後にそれ以上実行しません。私はあまりにも快適ではないコードのかなりのコードを書いており、これが失敗するような気がします。

もっと簡単な解決法がありますか、私は何かを誤解していますか?

更新 Peter R. suggestionに基づいて、私は以下を思いついた。より多くのコード行を許可しましたが、私は単一のスレッドオブジェクトを登録する必要はありません。すべてのスレッドが処分後にまだ実行されている場合、私は10秒間スリープ状態になり、この例を再度確認します。

private void WaitOnExecutingThreads() 
    { 
     foreach (var threadContext in _threadContexts) 
     { 
      threadContext.DisposeWaiter = new ManualResetEvent(false); 
      threadContext.Timer.Dispose(threadContext.DisposeWaiter); 
     } 

     while(_threadContexts.Count > 0) 
     { 
      for(var i = 0; i < _threadContexts.Count; i++) 
      { 
       var threadContext = _threadContexts[i]; 
       var isComplete = threadContext.DisposeWaiter.WaitOne(0); 
       if(isComplete) 
       { 
        Console.WriteLine(string.Format("{0}: {1} has completed", DateTime.Now, threadContext.Name)); 
        _threadContexts.RemoveAt(i); 
       } 
       else 
       { 
        Console.WriteLine(string.Format("{0}: {1} is still running", DateTime.Now, threadContext.Name)); 
       } 
      } 

      if (_threadContexts.Count > 0) 
      { 
       Thread.Sleep(new TimeSpan(0, 0, 10)); 
      } 
     } 
    } 
.... 
public class ThreadContext 
{ 
    public string Name { get; set; } 
    public Timer Timer { get; set; } 
    public WaitHandle DisposeWaiter { get; set; } 
} 

_

答えて

0

あなたのハンドラが完了していない場合は、あなたのManualResetEventsが通知されることはありません。したがって、イベントがシグナル状態にあるかどうかを簡単にテストできます。すなわち

var isComplete = waiters[0].WaitOne(0); 
+0

こんにちは、私は、ThreadPool.RegisterWaitForSingleObjectを使用してコールバックの完了を確認することができます。上記のコードをIsRunningデリゲートで使用するべきですか? 代わりにSystem.Timerに切り替えることを考えていますが、私はまだこれを使っていません。私は本当にThreadPool.RegisterWaitForSingleObjectを呼び出すのが好きではありません。これはオーバーヘッドのようです。 –

+0

パラメータが0のWaitOneコールは、実際には待機しません。すなわち、それは0MSを待っています。つまり、実際にあなたに走っている状態を与えます。 –

+0

こんにちはピーター、これに戻って、私はあなたの提案をはっきりと見ることができます。私はRegisterWaitForSingleObject呼び出しでハングアップしました。私はすでにwaiteventsのコレクションを持っているだけで、誰がまだメインスレッドで実行されているかを確認し続けることができます。これはWaitHandle.WaitAll呼び出しを取り除き、より簡単にします。 –

関連する問題