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

Posted

技术标签:

【中文标题】如果计时器运行时间超过计时器间隔,计时器将终止任务/作业【英文标题】: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 处启动计时器。当作业完成时,如果计时器已过,则启动另一个作业,否则等待计时器到时。 【参考方案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 以创建有用的答案并添加到社区中。

以上是关于如果计时器运行时间超过计时器间隔,计时器将终止任务/作业的主要内容,如果未能解决你的问题,请参考以下文章

C# - 在需要时从多个运行的计时器中处理一个特定的计时器[关闭]

C# - 计时器滴答时间比指定的间隔更短,导致重复作业并行运行

多个计时器已过问题

使用 GCD 计时器间隔播放声音未按预期运行

iOS 保证定时器进入后台依然运行

Timer 计时器 (SwiftUI中文文档手册)