简体   繁体   English

等待System.Threading.Timer回调完成,然后退出程序

[英]Wait for System.Threading.Timer Callbacks to Complete before Exiting Program

I have a List<System.Threading.Timer> . 我有一个List<System.Threading.Timer> Each Timer fires at a configurable interval (default 10 minutes). 每个计时器以可配置的间隔(默认10分钟)触发。 All call the same callback method (with a different parameter). 全部调用相同的回调方法(具有不同的参数)。 The callback method can take several seconds to complete it's work. 回调方法可能需要几秒钟才能完成其工作。

When the program terminates, it looks like execution of the callback method is immediately halted (am I seeing that correctly?). 程序终止时,回调方法似乎立即停止执行(我能正确看到吗?)。

How can I elegantly wait for any currently-executing callback methods to complete before exiting the program? 在退出程序之前,如何优雅地等待所有当前正在执行的回调方法完成?

You can Dispose all timers with WaitHandler parameter. 您可以使用WaitHandler参数处理所有计时器。 This handler will be signaled only when callback method is completed (as spec says: "The timer is not disposed until all currently queued callbacks have completed.") 仅在回调方法完成时才会向此处理程序发出信号(如规范所述:“直到所有当前排队的回调都完成后,计时器才会被处置。”)

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

Edit: @Peter underlined importance of the Dispose method return value. 编辑:@Peter强调了Dispose方法返回值的重要性。 It returns false when timer already disposed. 当定时器已经被处理时,它返回false。 To make sure this solutions stays reliable, I modified it to throw exception when Timer already disposed as we can't control in such case when its callback finishes, despite earlier disposal callback might still be running! 为了确保此解决方案保持可靠,我修改了它,使其在Timer已处置时引发异常,因为在这种情况下其回调完成时我们无法控制,尽管先前的处置回调可能仍在运行!

The accepted answer from Tomek is nice, but incomplete. Tomek接受的答案很好,但是不完整。 If the Dispose function returns false, it means that there is no need to wait for completion, as the thread is already finished. 如果Dispose函数返回false,则意味着不需要等待完成,因为线程已经完成。 If you make an attempt to wait on a WaitHandle in such a case, WaitAll will never return, so you created yourself a function that arbitrarily freezes your application/thread. 如果在这种情况下尝试等待WaitHandle,则WaitAll将永远不会返回,因此您为自己创建了一个函数,该函数可任意冻结应用程序/线程。

Here is how it should look: 这是它的外观:

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

You could use ManualResetEvents to block the main thread until any pending operations have completed. 您可以使用ManualResetEvents来阻塞主线程,直到完成所有挂起的操作。

for example if you would like all timers to execute at least once then you could have an System.Threading.ManualResetEvent[] array with the initial state set to non-signalled 例如,如果您希望所有计时器至少执行一次,则可以将System.Threading.ManualResetEvent[]数组的初始状态设置为未签名

So somewhere in your code you would have your timer setup and it's associated waithandle initialised. 因此,在代码中的某处您将设置计时器,并初始化关联的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);    

If you only wanted to wait for pending operations to finish then simply modify to something like this: 如果您只想等待未完成的操作完成,则只需修改为以下内容:

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

It's probably not possible to wait for exit in console application. 等待控制台应用程序中的退出可能是不可能的。

For windows forms application: 对于Windows窗体应用程序:

You can create a static running callback counter variable which will be increased each time the callback is started and decreased on exit. 您可以创建一个静态的正在运行的回调计数器变量,该变量将在每次启动回调时增加,并在退出时减小。 Of course, you should use lock when doing this. 当然,执行此操作时应使用锁。

And then you can check the corresponding event and whether wait for a counter to become 0 or just to cancel the exit. 然后,您可以检查相应的事件以及是否等待计数器变为0还是只是取消退出。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM