我有一個List<System.Threading.Timer>
。每個Timer都以可配置的時間間隔(默認10分鐘)觸發。所有調用相同的回調方法(使用不同的參數)。回調方法可能需要幾秒鐘才能完成它的工作。在退出程序之前等待System.Threading.Timer回調完成
程序終止時,看起來執行回調方法立即停止(我看到了正確的?)。
在退出程序之前,我該如何優雅地等待任何當前正在執行的回調方法?
我有一個List<System.Threading.Timer>
。每個Timer都以可配置的時間間隔(默認10分鐘)觸發。所有調用相同的回調方法(使用不同的參數)。回調方法可能需要幾秒鐘才能完成它的工作。在退出程序之前等待System.Threading.Timer回調完成
程序終止時,看起來執行回調方法立即停止(我看到了正確的?)。
在退出程序之前,我該如何優雅地等待任何當前正在執行的回調方法?
您可以用處置參數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已經處理時拋出異常,因爲在回調完成時我們無法控制這種情況,儘管早期處置回調可能仍在運行!
可能無法在控制檯應用程序中等待退出。
對於Windows窗體應用程序:
您可以創建一個靜態運行回調計數器變量,它會在每次回調開始,對出口下降的時間增加。當然,這樣做時你應該使用鎖定。
然後你可以檢查相應的事件和是否等待計數器變爲0或只是取消退出。
事實上,這是;-) http://msdn.microsoft.com/en-gb/library/system.console.cancelkeypress(v=vs。 80).aspx – Seb 2012-01-30 12:25:54
Tomek的解決方案在控制檯應用程序中正常工作。您的解決方案也可以在控制檯應用中使用。在主要退出之前,應用程序必須輪詢計數器等待它達到0。 – 2012-01-30 18:26:09
您可以使用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();
}
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());
}
不同意,只有在Timer上調用多次時,Dispose纔會返回false。 Timer的回調完成並不影響Dispose返回值。 – Tomek 2017-01-20 13:54:33
如果是單發計時器,您必須確保回調尚未開始,因爲您最終將永遠等待。 – 2017-08-22 09:51:47
我剛測試過這個,@Peter是對的:如果你多次處理,你可能會遇到死鎖。他的回答是正確的。 – 2017-01-19 16:04:05
我不同意@Peter解決方案,閱讀我的編輯。 – Tomek 2017-01-20 14:38:48