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

时间:2012-01-30 09:05:32

标签: c# multithreading timer

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

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

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

4 个答案:

答案 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或者只是取消退出。