2

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

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

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

下面的代码有效,但我想知道为什么在 ElapsedEventHandler 方法中作业/任务必须首先完成,然后我们可以设置新的计时器间隔。System.Timers.Timer 的 Elapsed 事件在间隔已过时引发。使用 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();
        }
    }
}

结果:

在此处输入图像描述

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

在此处输入图像描述

为什么我们不能在方法 OnTimedEvent 开始时设置计时器间隔。如果我们在任务/作业运行时间超过计时器间隔的情况下这样做,线程只会终止该作业。如果有人有一些想法,我将不胜感激?这是否与线程与主线程(哪个计时器运行)的同步有关?当我们调用方法 OnTimedEvent 时,计时器将不会再次调用该方法,因为它具有 AutoReset = false,那么在设置计时器属性的地方有什么区别?

4

2 回答 2

4
   t.Interval = intervalMiliseconds;

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

如果 Enabled 和 AutoReset 都设置为 false,并且计时器之前已启用,则设置 Interval 属性会导致 Elapsed 事件引发一次,就像 Enabled 属性已设置为 true 一样。要设置间隔而不引发事件,您可以将 AutoReset 属性临时设置为 true。

这是一个相当愚蠢的黑客,但确实有效。只是延迟分配值肯定是更好的方法。早点做不会给你带来任何好处,除了麻烦之外,由于你设置了 AutoReset = false,因此计时器无论如何都不会滴答作响。

System.Threading.Timer 是更好的计时器类,具有更少的怪癖。它不会在回调方法中没有任何诊断的情况下吞下异常。您的代码对哪个非常敏感,计时器将停止计时,因为异常绕过了 t.Start() 调用,您不知道为什么。强力推荐。

于 2013-08-04T15:30:26.723 回答
1

!! No and never Timer kill task/job if it runs longer than timer interval !!

if processing time of task/job is less than timer interval, after timer interval/span. if processing time of job/task is more than timer interval, start next job/task after timer interval/span into a new Thread.

so for minimizing idle time you should keep timer interval small. In System.Timers.Timer class internally already Threading Implemented. so don`t need to implements threading.

于 2017-07-14T09:37:51.657 回答