我有List<System.Threading.Timer>
。每个Timer以可配置的间隔(默认为10分钟)触发。 All调用相同的回调方法(使用不同的参数)。回调方法可能需要几秒钟才能完成它的工作。
当程序终止时,看起来回调方法的执行会立即停止(我看到了吗?)。
如何在退出程序之前优雅地等待任何当前正在执行的回调方法?
答案 0 :(得分:17)
您可以使用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已经处理时抛出异常,因为在回调完成时我们无法控制这种情况,尽管早期的处理回调可能仍在运行!
答案 1 :(得分:4)
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());
}
答案 2 :(得分:1)
您可以使用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();
}
答案 3 :(得分:-2)
可能无法在控制台应用程序中等待退出。
对于Windows窗体应用程序:
您可以创建一个静态运行的回调计数器变量,每次回调开始时都会增加,并在退出时减少。当然,在执行此操作时应使用锁定。
然后你可以检查相应的事件以及是否等待计数器变为0或者只是取消退出。