在退出程序之前等待System.Threading.Timer回调完成

我有一个List 。 每个Timer以可配置的间隔(默认为10分钟)触发。 全部调用相同的回调方法(使用不同的参数)。 回调方法可能需要几秒钟才能完成它的工作。

当程序终止时,看起来回调方法的执行会立即停止(我是否正确看到了?)。

在退出程序之前,如何优雅地等待任何当前正在执行的回调方法?

您可以使用WaitHandler参数处理所有计时器。 只有当回调方法完成时才会发出此处理程序的信号(如规范所述:“在所有当前排队的回调都完成之前,不会释放计时器。”)

 void WaitUntilCompleted(List myTimers) { List waitHnd = new List(); 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已经处理时抛出exception,因为在回调完成时我们无法控制这种情况,尽管早期的处理回调可能仍在运行!

Tomek接受的答案很好,但不完整。 如果Dispose函数返回false,则表示不需要等待完成,因为线程已经完成。 如果在这种情况下尝试等待WaitHandle,WaitAll将永远不会返回,所以你自己创建了一个任意冻结你的应用程序/线程的函数。

以下是它的外观:

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

您可以使用ManualResetEvents阻止主线程,直到任何挂起的操作完成。

例如,如果您希望所有计时器至少执行一次,那么您可以拥有一个System.Threading.ManualResetEvent[]数组,其初始状态设置为无信号

因此,在代码中的某个位置,您可以设置定时器并将其初始化为waithandle。

 // 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(); } 

在控制台应用程序中等待退出可能是不可能的。

对于Windows窗体应用程序:

您可以创建一个静态运行的回调计数器变量,该变量将在每次启动回调时增加,在退出时减少。 当然,在执行此操作时应使用锁定。

然后你可以检查相应的事件以及是否等待计数器变为0或者只是取消退出。