2012-01-30 163 views
11

我有一個List<System.Threading.Timer>。每個Timer都以可配置的時間間隔(默認10分鐘)觸發。所有調用相同的回調方法(使用不同的參數)。回調方法可能需要幾秒鐘才能完成它的工作。在退出程序之前等待System.Threading.Timer回調完成

程序終止時,看起來執行回調方法立即停止(我看到了正確的?)。

在退出程序之前,我該如何優雅地等待任何當前正在執行的回調方法?

回答

16

您可以用處置參數WaitHandler所有定時器。當完成回調方法該處理器將只發出信號(如規範說:「計時器沒有設置,直到所有當前排隊的回調已經完成」)

void WaitUntilCompleted(List<Timer> myTimers) 
{ 
    List<WaitHandle> waitHnd = new List<WaitHandle>(); 
    foreach (var timer in myTimers) 
    { 
     WaitHandle h = new AutoResetEvent(false); 
     if(!timer.Dispose(h)) throw new Exception("Timer already disposed."); 
     waitHnd.Add(h); 
    } 
    WaitHandle.WaitAll(waitHnd.ToArray()); 
} 

編輯: @Peter強調Dispose方法的重要性返回值。當計時器已經處置時它返回false。爲了確保這些解決方案保持可靠,我修改了它,以便在Timer已經處理時拋出異常,因爲在回調完成時我們無法控制這種情況,儘管早期處置回調可能仍在運行!

+0

我剛測試過這個,@Peter是對的:如果你多次處理,你可能會遇到死鎖。他的回答是正確的。 – 2017-01-19 16:04:05

+0

我不同意@Peter解決方案,閱讀我的編輯。 – Tomek 2017-01-20 14:38:48

-2

可能無法在控制檯應用程序中等待退出。

對於Windows窗體應用程序:

您可以創建一個靜態運行回調計數器變量,它會在每次回調開始,對出口下降的時間增加。當然,這樣做時你應該使用鎖定。

然後你可以檢查相應的事件和是否等待計數器變爲0或只是取消退出。

+0

事實上,這是;-) http://msdn.microsoft.com/en-gb/library/system.console.cancelkeypress(v=vs。 80).aspx – Seb 2012-01-30 12:25:54

+0

Tomek的解決方案在控制檯應用程序中正常工作。您的解決方案也可以在控制檯應用中使用。在主要退出之前,應用程序必須輪詢計數器等待它達到0。 – 2012-01-30 18:26:09

1

您可以使用ManualResetEvents來阻止主線程,直到任何未完成的操作完成。

例如,如果你想所有定時器來執行至少一次,那麼你可以有一個System.Threading.ManualResetEvent[]陣列設置爲初始狀態無信號在你的代碼

所以地方,你將有你的計時器設置並且它關聯了等待句柄初始化。

// in main setup method.. 
int frequencyInMs = 600000; //10 mins 
Timer timer = new Timer(); 
timer.Elapsed += (s, e) => MyExecute(); 
myTimers.Add(timer) 

ManualResetEvent[] _waithandles = new ManualResetEvent[10]; 
_waithandles[0] = new ManualResetEvent(false); 

// Other timers ... 
timer = new Timer(); 
timer.Elapsed += (s, e) => MyOtherExecute(); 
myTimers.Add(timer)   
_waithandles[1] = new ManualResetEvent(false); 
// etc, and so on for all timers 

// then in each method that gets executed by the timer 
// simply set ManualReset event to signalled that will unblock it. 
private void MyExecute() 
{ 
    // do all my logic then when done signal the manual reset event 
    _waithandles[0].Set(); 
} 

// In your main before exiting, this will cause the main thread to wait 
// until all ManualResetEvents are set to signalled 
WaitHandle.WaitAll(_waithandles);  

如果你只是想等待掛起操作完成,然後簡單地修改,以這樣的:

_waithandles[0] = new ManualResetEvent(true); // initial state set to non blocking. 

private void MyExecute() 
{ 
    _waithandles[0].Reset(); // set this waithandle to block.. 

    // do all my logic then when done signal the manual reset event 
    _waithandles[0].Set(); 
} 
3

Tomek公認的答案很好,但不完整。如果Dispose函數返回false,則意味着不需要等待完成,因爲線程已經完成。如果你試圖在這種情況下等待WaitHandle,WaitAll永遠不會返回,所以你創建了一個函數來任意凍結你的應用程序/線程。

下面是它應該如何看:

void WaitUntilCompleted(List<Timer> myTimers) 
    { 
     List<WaitHandle> waitHnd = new List<WaitHandle>(); 
     foreach (var timer in myTimers) 
     { 
      WaitHandle h = new AutoResetEvent(false); 
      if (timer.Dispose(h)) 
      { 
       waitHnd.Add(h); 
      } 
     } 
     WaitHandle.WaitAll(waitHnd.ToArray()); 
    } 
+1

不同意,只有在Timer上調用多次時,Dispose纔會返回false。 Timer的回調完成並不影響Dispose返回值。 – Tomek 2017-01-20 13:54:33

+0

如果是單發計時器,您必須確保回調尚未開始,因爲您最終將永遠等待。 – 2017-08-22 09:51:47

相關問題