如果任务/作业的运行时间超过计时器间隔,则计时器将终

时间:2013-08-02 07:55:24

标签: c# .net timer threadpool c#-2.0

我想制作一个行为如下的计时器:

  • 如果任务/作业的处理时间小于计时器间隔,则启动计时器(timer.interval - 处理时间作业/作业)

  • 如果作业/任务的处理时间超过计时器间隔,立即开始下一个作业/任务

下面的代码有效,但我想知道为什么在ElapsedEventHandler方法中必须首先完成作业/任务,然后我们可以设置新的计时器间隔。经过间隔后,将引发System.Timers.Timer的经过事件。使用选项AutoReset = false,我们设置Timer在第一个Interval过去之后仅引发一次Elapsed事件。然后我们必须手动调用Timer.Start()再次启动它。

using System;
using System.Timers;

 namespace TestTimer
 {
    class Program
    {
        private static Timer t;
        private static double intervalMiliseconds;

        static void Main(string[] args)
        {
            intervalMiliseconds = 5000; // 5 seconds

            t           = new Timer();
            t.Interval  = intervalMiliseconds;
            t.AutoReset = false;
            t.Elapsed  += new ElapsedEventHandler(OnTimedEvent);
            t.Start();

            log("Timer started at " + DateTime.Now.ToString());
            log("Interval is: " + defaultIntervalMiliseconds);
            Console.ReadKey();
        }

        private static void log(string text)
        {
            Console.WriteLine(text + "\n");
        }

        private static void OnTimedEvent(object source, ElapsedEventArgs e)
        {
            // if t.Interval is set here thread just kills the job if it
            // runs longer than interval
            t.Interval = intervalMiliseconds;
            log("ElapsedEvent triggered at " + DateTime.Now.ToString());

            // job
            DateTime startTime = DateTime.Now;
            log("job started" );
            System.Threading.Thread.Sleep(8000); // 8 sec
            log("job ended" );
            TimeSpan jobTime = DateTime.Now.Subtract(startTime);
            log("job took " + jobTime.TotalSeconds + " seconds");

            // if we set t.Interval here it works so first the job
            // must be done and than we can set timer interval ? why ?
            //t.Interval = intervalMiliseconds;

            if (jobTime.TotalMilliseconds < t.Interval)
            {
                t.Interval = t.Interval - jobTime.TotalMilliseconds;
                log("Job ended Earlier starting Event in: " + t.Interval);
            }
            else
            {
                t.Interval = 100;
                log("Job overpass interval. Current time: " +
                     DateTime.Now.ToString());
            }

            t.AutoReset = false;
            t.Start();
        }
    }
}

结果:

enter image description here

如果我们在方法OnTimedEvent的开头注释t.Interval并在作业完成后取消注释t.Interval,一切正常。结果:

enter image description here

为什么我们不能在方法OnTimedEvent的开头设置定时器间隔。如果我们这样做,如果任务/作业运行的时间超过了计时器间隔,那么线程就会杀死该作业。如果有人有想法,我真的很感激?这是否与主线程的线程同步(哪个定时器运行)有关?当我们调用方法OnTimedEvent时,计时器不会再次调用该方法,因为它具有AutoReset = false,它在我们设置计时器属性时会有什么不同?

2 个答案:

答案 0 :(得分:4)

   t.Interval = intervalMiliseconds;

这确实是麻烦制造者。这是非常不直观的行为,Timer.Interval的MSDN article在注释中特别警告:

  

如果Enabled和AutoReset都设置为false,并且先前已启用计时器,则设置Interval属性会导致Elapsed事件被提升一次,就像Enabled属性已设置为true一样。要在不引发事件的情况下设置间隔,可以暂时将AutoReset属性设置为true。

这是一个相当愚蠢的黑客,但确实有效。只是推迟分配值肯定是更好的方法。尽早做这件事并没有给你买任何东西,除了麻烦之外,由于你有AutoReset = false,计时器不会打勾。

System.Threading.Timer是更好的计时器类,具有更少的怪癖。如果没有在回调方法中进行任何诊断,它就不会吞下异常。您的代码非常敏感,定时器将停止滴答,因为异常会绕过t.Start()调用,您将不知道为什么。强烈推荐。

答案 1 :(得分:0)

<强> !!如果运行时间超过计时器间隔,则不会且永远不会定时器终止任务/作业!!

如果任务/作业的处理时间小于计时器间隔, 在定时器间隔/跨度之后。 如果作业/任务的处理时间超过计时器间隔, 在定时器间隔/跨越到新线程后启动下一个作业/任务。

因此,为了最大限度地减少空闲时间,您应该将计时器间隔设置得很小。  在System.Timers.Timer类内部已经Threading Implemented。所以不需要实现线程。