我想制作一个行为如下的计时器:
如果任务/作业的处理时间小于计时器间隔,则启动计时器(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();
}
}
}
结果:
如果我们在方法OnTimedEvent的开头注释t.Interval并在作业完成后取消注释t.Interval,一切正常。结果:
为什么我们不能在方法OnTimedEvent的开头设置定时器间隔。如果我们这样做,如果任务/作业运行的时间超过了计时器间隔,那么线程就会杀死该作业。如果有人有想法,我真的很感激?这是否与主线程的线程同步(哪个定时器运行)有关?当我们调用方法OnTimedEvent时,计时器不会再次调用该方法,因为它具有AutoReset = false,它在我们设置计时器属性时会有什么不同?
答案 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。所以不需要实现线程。