【问题标题】:Timer kills task/job if it runs longer than timer interval如果计时器运行时间超过计时器间隔,计时器将终止任务/作业
【发布时间】:2013-08-03 09:56:17
【问题描述】:

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

  • 如果任务/作业的处理时间小于计时器间隔,则在(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,那么设置定时器属性有什么区别?

【问题讨论】:

  • 我还没有读完你的整个问题,但为什么不在工作的 start 处启动计时器。当作业完成时,如果计时器已过,则启动另一个作业,否则等待计时器到时。

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


【解决方案1】:
   t.Interval = intervalMiliseconds;

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

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

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

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

【讨论】:

    【解决方案2】:

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

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

    因此,为了尽量减少空闲时间,您应该保持较小的计时器间隔。 在 System.Timers.Timer 类内部已经实现了 Threading。所以不需要实现线程。

    【讨论】:

    • 您好,欢迎来到 Stack Overflow,首先感谢您花时间回答。请花点时间通过welcome tour 了解您的方法,阅读How to Answer 以创建有用的答案并添加到社区中。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2019-10-04
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2019-03-02
    • 1970-01-01
    相关资源
    最近更新 更多