首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >等待System.Threading.Timer回调完成后再退出程序

等待System.Threading.Timer回调完成后再退出程序
EN

Stack Overflow用户
提问于 2012-01-30 17:05:32
回答 4查看 10.7K关注 0票数 12

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

当程序终止时,回调方法的执行看起来会立即停止(我没看错吧?)

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

EN

回答 4

Stack Overflow用户

回答已采纳

发布于 2012-01-30 18:26:23

你可以用WaitHandler参数来处理所有的定时器。此处理程序仅在回调方法完成时才会发出信号(正如spec所说:“计时器直到所有当前排队的回调都完成后才会被释放”)。

代码语言:javascript
复制
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。为了确保这个解决方案保持可靠,我修改了它,当计时器已经释放时抛出异常,因为在这种情况下我们无法控制它的回调何时结束,尽管早期的释放回调可能仍然在运行!

票数 17
EN

Stack Overflow用户

发布于 2016-09-15 21:46:52

Tomek给出的公认答案很好,但并不完整。如果Dispose函数返回false,则意味着不需要等待完成,因为线程已经完成。如果您在这种情况下尝试等待WaitHandle,WaitAll将永远不会返回,因此您自己创建了一个函数来任意冻结您的应用程序/线程。

下面是它应该是什么样子:

代码语言:javascript
复制
    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());
    }
票数 6
EN

Stack Overflow用户

发布于 2012-01-30 17:48:39

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

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

因此,在你的代码中的某个地方,你可以设置你的计时器,并且初始化相关的waithandle。

代码语言:javascript
复制
// 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);    

如果您只想等待挂起的操作完成,那么只需将其修改为如下所示:

代码语言:javascript
复制
_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(); 
}
票数 1
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/9061484

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档