简体   繁体   English

C# - 如何暂停应用程序直到计时器完成?

[英]C# - How to pause application until timer is finished?

I have an application that I need to have wait a specific amount of time, but I also need to be able to cancel the current operation if needed.我有一个需要等待特定时间的应用程序,但如果需要,我还需要能够取消当前操作。 I have the following code:我有以下代码:

private void waitTimer(int days)
{
    TimeSpan waitTime = TimeSpan.FromDays(days);
    System.Timers.Timer timer = new System.Timers.Timer(waitTime.TotalMilliseconds);   // Wait for some number of milliseconds
    timer.Enabled = true;
    timer.Start();
    timer.Elapsed += new ElapsedEventHandler(OnTimedEvent); // Subscribe to event handler

    while (!TimerSettings.TimerFinished && !quitToken.IsCancellationRequested);  // Loop forever untill timer is finished or operation is cancled. 

    timer.Elapsed -= new ElapsedEventHandler(OnTimedEvent); // Unsubscribe

    DoWork(); // Do work when timer finishes.......
}

Below is the event handler for the timer finished event:下面是计时器完成事件的事件处理程序:

private void OnTimedEvent(object obj, ElapsedEventArgs e)
{
    TimerSettings.TimerFinished = true;
}

The while loop just loops infinitely until the timer is finished or until a cancelation request is put in. I want to retain this functionality but I would rather not loop forever while waiting for the timer to finish. while 循环只是无限循环,直到计时器完成或直到取消请求被放入。我想保留这个功能,但我不想在等待计时器完成时永远循环。 My timer can be set to run on an interval of multiple days so it doesn't make sense to loop for so long.我的计时器可以设置为以多天为间隔运行,因此循环这么长时间是没有意义的。

Is there another way of doing this?还有另一种方法吗?

I know I could do:我知道我可以这样做:

Thread.Sleep(runDuration.TotalMilliseconds);

However, this would be blocking and I would not be able to put in a cancelation request.但是,这会阻塞,我将无法提交取消请求。

EDIT: So in order to elaborate on what/why I need to pause here is a more detailed explination of my application.编辑:因此,为了详细说明我需要在此处暂停的内容/原因,请对我的应用程序进行更详细的说明。 Basically I want to have an application that performs "work" on a regular interval.基本上我想要一个定期执行“工作”的应用程序。 So based on one of the answers provided below, if I did something like this:所以根据下面提供的答案之一,如果我做了这样的事情:

class Program
{
    // Do something in this method forever on a regular interval 
    //(could be every 5min or maybe every 5days, it's up to the user)
    static void Main(string[] args)
    {
        while(true)
        {
          if(args?.Length > 0)
              waitTimer(args[0]);
          else 
              wiatTimer(TimeSpan.FromDays(1).TotalSeconds); // Default to one day interval
        }             
    }

private void waitTimer(int numIntervals)
{
    this.ElapsedIntervals = 0;
    this.IntervalsRequired = numIntervals;
    this.timer = new System.Timers.Timer(1000);   // raise the elapsed event every second
    timer.Elapsed += new ElapsedEventHandler(OnTimedEvent); // Subscribe to event handler
    //timer.Enabled = true; timer.Start() does this for you, don't do this
    timer.Start();
    //thats all here
}

 private void OnTimedEvent(object obj, ElapsedEventArgs e)
 {
    this.ElapsedIntervals += 1;
    if(this.CancelRequested)
    {
       this.ElapsedIntervals = 0;
       this.timer.Stop();
       return;
    }
    if(this.ElapsedIntervals >= this.IntervalsRequired)
    {
       this.ElapsedIntervals = 0;
       this.timer.Stop();
       DoWork();   // This is where my work gets done.......
      return;
    }
  }
}

Then my service/console app would start and go into an infinite loop that just sets timers all day long.然后我的服务/控制台应用程序将启动并进入一个无限循环,整天只设置计时器。 Previously, I was actually halting execution of any other code at:以前,我实际上是在以下位置停止执行任何其他代码:

while (!TimerSettings.TimerFinished && !quitToken.IsCancellationRequested);

Which at least worked, but as mentioned, can possibly be resource intensive way to pause a thread.这至少有效,但如前所述,可能是暂停线程的资源密集型方式。 Basically what I really need is a way to block my thread untill the timer is up.基本上我真正需要的是一种阻止我的线程直到计时器结束的方法。

EDIT2: This is my final implementation that seems to work for me using a wait handle... EDIT2:这是我使用等待句柄似乎对我有用的最终实现...

class TimerClass
{
    /// <summary>
    /// Initialize new timer. To set timer duration,
    /// either set the "IntervalMinutes" app config 
    /// parameter, or pass in the duration timespan.
    /// </summary>
    /// <param name="time"></param>
    internal bool StartTimer(CancellationToken quitToken, TimeSpan? duration = null)
    {
        TimeSpan runDuration = new TimeSpan();
        runDuration = duration == null ? GetTimerSpan() : default(TimeSpan);

        if (runDuration != default(TimeSpan))
        {
            WaitTimer(runDuration); // Waits for the runduration to pass
        }
        return true;
    }

    /// <summary>
    /// Get duration to run the timer for.
    /// </summary>
    internal TimeSpan GetTimerSpan()
    {
        TimerSettings.Mode = App.Settings.Mode;
        DateTime scheduledTime = new DateTime();

        switch (TimerSettings.Mode)
        {
            case "Daily":
                scheduledTime = DateTime.ParseExact(App.Settings.ScheduledTime, "HH:mm:ss", CultureInfo.InvariantCulture);
                if (scheduledTime > DateTime.Now)
                    TimerSettings.TimerInterval = scheduledTime - DateTime.Now;
                else
                    TimerSettings.TimerInterval = (scheduledTime + TimeSpan.FromDays(1)) - DateTime.Now;
                break;
            case "Interval":
                double IntervalMin = double.TryParse(App.Settings.PollingIntervalMinutes, out IntervalMin) ? IntervalMin : 15.00;
                int IntervalSec = Convert.ToInt32(Math.Round(60 * IntervalMin));
                TimeSpan RunInterval = new TimeSpan(0, 0, IntervalSec);
                TimerSettings.TimerInterval = RunInterval;
                break;
            case "Manual":
                TimerSettings.TimerInterval = TimeSpan.FromMilliseconds(0);
                break;
            default:
                TimerSettings.TimerInterval = (DateTime.Today + TimeSpan.FromDays(1)) - DateTime.Now;
                break;
        }
        return TimerSettings.TimerInterval;
    }

    /// <summary>
    /// Event handler for each timer tick.
    /// </summary>
    /// <param name="obj"></param>
    /// <param name="e"></param>
    private void OnTimedEvent(object obj, ElapsedEventArgs e)
    {
        ElapsedIntervals += 1;
        if (CancelRequested.IsCancellationRequested) // If the application was cancled
        {
            ElapsedIntervals = 0;
            timer.Stop();
            WaitHandle.Set();
            return;
        }
        if (ElapsedIntervals >= IntervalsRequired) // If time is up
        {
            ElapsedIntervals = 0;
            timer.Stop();
            WaitHandle.Set();
            return;
        }
    }

    /// <summary>
    /// Timer method to wait for a
    /// specified duration to pass. 
    /// </summary>
    /// <param name="span"></param>
    private void WaitTimer(TimeSpan span)
    {
        WaitHandle = new AutoResetEvent(false);
        int tickDuration = 1000;  // Number of milliseconds for each tick
        IntervalsRequired = Convert.ToInt64(span.TotalMilliseconds / (tickDuration > 0 ? tickDuration : 0.01));
        timer = new System.Timers.Timer(tickDuration);          // Raise the elapsed event every tick
        timer.Elapsed += new ElapsedEventHandler(OnTimedEvent); // Subscribe to event handler for when each tick is complete
        timer.Start();           // Start ticking
        WaitHandle.WaitOne();    // Halt the main thread untill span is reached
    }


    // Timer parameters: 
    private static long ElapsedIntervals { get; set; }
    private static long IntervalsRequired { get; set; }
    private static System.Timers.Timer timer { get; set; }
    private static CancellationToken CancelRequested { get; set; }
    private static string Mode { get; set; }
    private static TimeSpan TimerInterval { get; set; }
    private static EventWaitHandle WaitHandle { get; set; }
}

internal static class TimerSettings
{
    internal static string Mode { get; set; }
    internal static TimeSpan TimerInterval { get; set; }
}

You should look at the Timer.Elapsed Event documentation.您应该查看 Timer.Elapsed 事件文档。 This event will be raised repeatedly every time the interval elapses while the AutoReset property is set to true (which is default).当 AutoReset 属性设置为 true(这是默认值)时,每次间隔过去时都会重复引发此事件。 I would keep your own count of how many intervals have elapsed and compare it to the required elapsed intervals in this event handler to check whether it is time to stop the timer.我会自己计算已经过去了多少时间间隔,并将其与此事件处理程序中所需的经过时间间隔进行比较,以检查是否是停止计时器的时间。 In that event, you can also handle cancellation.在这种情况下,您还可以处理取消。 If your timer finishes its required number of intervals, you may call your doWork function from that event handler.如果您的计时器完成了所需的间隔数,您可以从该事件处理程序调用 doWork 函数。

private void waitTimer(int numIntervals)
{
    this.ElapsedIntervals = 0;
    this.IntervalsRequired = numIntervals;
    this.timer = new System.Timers.Timer(1000);   // raise the elapsed event every second
    timer.Elapsed += new ElapsedEventHandler(OnTimedEvent); // Subscribe to event handler
    //timer.Enabled = true; timer.Start() does this for you, don't do this
    timer.Start();
    //thats all here
}

private void OnTimedEvent(object obj, ElapsedEventArgs e)
{
    this.ElapsedIntervals += 1;
    if(this.CancelRequested)
    {
        this.ElapsedIntervals = 0;
        this.timer.Stop();
        return;
    }
    if(this.ElapsedIntervals >= this.IntervalsRequired)
    {
       this.ElapsedIntervals = 0;
       this.timer.Stop();
       DoWork();
       return;
    }
}

https://msdn.microsoft.com/en-us/library/system.timers.timer.elapsed(v=vs.110).aspx https://msdn.microsoft.com/en-us/library/system.timers.timer.elapsed(v=vs.110).aspx

As I see it, with regards to "pausing", there are two reasons to want to pause and I am unsure which reason is yours:在我看来,关于“暂停”,有两个原因想要暂停,我不确定哪个原因是你的:

  1. You want to prevent the application from "finishing" execution and terminating normally.您想阻止应用程序“完成”执行和正常终止。
  2. You want to hold off on executing other code until the number of required intervals has elapsed您想推迟执行其他代码,直到所需的时间间隔数已过

If your reason is #2, then this answer is complete.如果你的理由是#2,那么这个答案就完成了。

First of all: "you absolutely do not(!) want to 'busy-wait' for anything!"首先:“你绝对不想(!)想要'忙-等待'任何东西!” (BAD Dog! NO Biscuit!) (坏狗!没有饼干!)

Ahem...咳咳...

A practical solution to this problem is to perform a timed wait on a semaphore (or any other suitable mutual-exclusion object ...), instead of using an actual Timer.这个问题的一个实际解决方案是对信号量(或任何其他合适的互斥对象......)执行定时等待,而不是使用实际的 Timer。 If you need to bust-out of the wait before it is finished, just strobe the thing upon which it is waiting.如果您需要在等待完成之前退出等待,只需选通它正在等待的东西即可。

The critical problem with your present "solution" is that it will slam the process into 100% CPU utilization, altogether wastefully.您目前的“解决方案”的关键问题是它会将进程猛烈地消耗到100% 的 CPU 利用率,这完全是浪费。 Never do that!永远不要那样做!

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

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