间隔多月的定时器

问题描述 投票:0回答:5

我有一个 Windows 服务处理由 System.Timers.Timer 触发的事件。 我想将该计时器的间隔设置为 3 个月。

System.Timers.Timer 的 Interval 属性是一个以毫秒为单位的 Int32,并且 Int32.MaxValue 小于 3 个月(以毫秒为单位)。

我该怎么办?

c# timer
5个回答
10
投票

你会重新思考你的设计。存储您下次要执行事件的时间(例如注册表、文件、数据库...),然后定期唤醒以检查该时间是否已过。即使您可以将 System.Timers.Timer 设置为 3 个月,系统也可能会在计时器关闭之前重新启动,并且您会丢失事件。

另一种选择是使用由 Windows Scheduler 执行的计划作业。它将运行一个小程序,向您的服务发送一条消息,表明事件已发生。与定期醒来检查 3 个月是否已经过去相比,这会占用更少的资源,但更复杂。


2
投票

有同样的问题,只不过它是一个通用系统,可以以完全可调的时间间隔(从毫秒到几个月)做同样的事情。嗯,是“应该”的。事实证明,由于这个原因,它确实在大于 24.8 天的间隔上出现了混乱。 就我而言,“重新考虑方法”是不可能的,因为这只是一个更大的 Windows 服务系统的一个小问题子案例。

解决方案相当简单,但请注意,我有一个辅助系统,它为我提供了下一次执行:

DateTime

;计时器的工作只是匹配实际执行任务,因此它只需从中减去

DateTime.Now
即可计算出时间间隔。
在这种情况下,我需要做的就是在计时器对象旁边保留一个溢出布尔值,并在检查该间隔时进行设置 

Int32.MaxValue

:

private Timer _timer;
private Boolean _overflow;

// Amount of days between executions.
private Int64 _intervalDays;
// Offset time inside day. Is prepared in advance to be lower than a full day.
private Int32 _offsetInDay;
// Last time the operation was executed.
private DateTime _lastTime;

private void QueueNextTime(DateTime thisTime)
{
    TimeSpan interval = this.GetNextRunTime(thisTime) - DateTime.Now;
    // Not using TotalMilliseconds since it adds an extra conversion to Double.
    Int64 intervalMilliSeconds = interval.Ticks / 10000;
    if (intervalMilliSeconds <= 0)
        intervalMilliSeconds = 1;
    // If interval greater than Int32.MaxValue, set the boolean to skip the next
    // run. The interval will be topped at Int32.MaxValue. The TimerElapsed
    // function will call this function again anyway, so no need to store any
    // information on how much is left. It'll just repeat until the overflow
    // status is 'false'.
    bool wasOverflow = this._overflow;
    this._overflow = intervalMilliSeconds > Int32.MaxValue;
    this._timer.Interval = Math.Min(intervalMilliSeconds, Int32.MaxValue);
    if (!wasOverflow)
    {
        // Execution actually occurred. Save time.
        this._lastTime = thisTime;
    }
    this._timer.Start();
}

// The actual system I used was more advanced in choices of interval and
// offset types (could do stuff like "12th of each month"), but this is
// the basic gist of it, simplified to a system that's basically
// "every x days, at HH:mm:ss"
private DateTime NextRunTime(DateTime lastRunTime)
{
    // Take start of execution day as basis.
    DateTime next = new DateTime(_lastTime.Year, _lastTime.Month, _lastTime.Day);
    // start of day after current run day.
    DateTime afterLastRunDay =
        new DateTime(lastRunTime.Year, lastRunTime.Month, lastRunTime.Day)
        .AddDays(1);
    // Full system can load "_lastTime" from settings to resume after a cold
    // start. The loop ensures the schedule is retained as if it never stopped.
    while (next <= afterLastRunDay)
    {
        next = next.AddDays(_intervalDays);
    }
    return next.AddMilliseconds(_offsetInDay);
}

// The function linked to _timer.Elapsed
private void TimerElapsed(object sender, ElapsedEventArgs e)
{
    this._timer.Stop();
    if (this._overflow)
    {
        QueueNextTime(e.SignalTime);
        return;
    }

    // Execute tasks
    // ...
    // ...

    // schedule next execution, based on when this execution run started.
    QueueNextTime(e.SignalTime);
}

当然这是简化的;真实的系统有 try/catch 和一个根据外部停止命令中止的系统。但这就是要点。


1
投票
http://quartznet.sourceforge.net/

您可以使用类似 CRON 的语法 -

http://quartznet.sourceforge.net/tutorial/lesson_6.html

您可以轻松地将相关作业状态存储到数据库中(以防万一机器死掉,它会死掉) - http://quartznet.sourceforge.net/tutorial/lesson_9.html,还有更多用处。 我已经在多个生产系统上使用了 Quartz.net,据我所知,这些流程至今仍在运行:)

为什么不使用 System.Timer -

http://quartznet.sourceforge.net/faq.html#whynottimer

祝你好运!


1
投票

如果您想以 3 个月的间隔触发事件,则必须保留退出应用程序时计时器所用的总时间的信息。

已更新

所以你必须把你的间隔分成几个部分,并在每次经过时设置一个计数器并递增它。每次经过时检查它,直到达到3个月。

例如

int interval = 10 * 24 * 60 * 60 * 1000; // Time interval for 10 days int counter = 0; private void timer1_Elapsed(object sender, System.Timers.ElapsedEventArgs e) { counter++; if (counter == 9) { //do your task for 3 months //since counter increments 9 times at interval of 10 days //so 9*10=90 days i.e. nearly equal to 3 months counter = 0; } }



0
投票

© www.soinside.com 2019 - 2024. All rights reserved.