c# - System.Threading.Timer 调用每天漂移几秒钟

标签 c# time windows-services timer

我有一个始终运行的服务,它有一个计时器,每天凌晨 2 点执行特定操作。

TimeSpan runTime = new TimeSpan(2, 0, 0); // 2 AM
TimeSpan timeToFirstRun = runTime - DateTime.Now.TimeOfDay;

if (timeToFirstRun.TotalHours < 0)
{
    timeToFirstRun += TimeSpan.FromDays(1.0);
}

_dailyNodalRunTimer = new Timer(
    RunNodalDailyBatch,
    null,
    timeToFirstRun,
    TimeSpan.FromDays(1.0)); //repeat event daily

当服务第一次启动时,初始化代码被调用一次,在过去的几天里,我记录了 Timer 触发的时间:

2011-05-21 02:00:01.580
2011-05-22 02:00:03.840
...
2011-05-31 02:00:25.227
2011-06-01 02:00:27.423
2011-06-02 02:00:29.847

如您所见,它每天漂移 2 秒,离它应该发射的时间(凌晨 2 点)越来越远。

是我用错了还是这个 Timer 的设计不准确?我可以每天重新创建计时器,或者让它每隔一段时间触发一次,然后反复检查我是否要执行该操作,但这似乎有点老套。

编辑

我尝试使用 System.Timers.Timer,它似乎有同样的问题。重置间隔是因为您无法像在 System.Threading.Timer 中那样在 System.Timers.Timer 中的第一个刻度之前安排初始时间

int secondsInterval = 5;

double secondsUntilRunFirstRun = secondsInterval - (DateTime.Now.TimeOfDay.TotalSeconds % secondsInterval);
var timer = new System.Timers.Timer(secondsUntilRunFirstRun * 1000.0);
timer.AutoReset = true;
timer.Elapsed += (sender, e) =>
    {
        Console.WriteLine(DateTime.Now.ToString("hh:mm:ss.fff"));

        if (timer.Interval != (secondsInterval * 1000.0))
            timer.Interval = secondsInterval * 1000.0;
    };
timer.Start();

产生以下时间,你可以看到它们是如何轻微漂移的:

06:47:40.020
06:47:45.035
06:47:50.051
...
06:49:40.215
06:49:45.223
06:49:50.232

所以我想最好的方法真的是在滴答处理程序中重新安排计时器?以下代码在约 15 毫秒内以固定间隔产生滴答声

double secondsUntilRunFirstRun = secondsInterval - (DateTime.Now.TimeOfDay.TotalSeconds % secondsInterval);

var timer = new System.Timers.Timer(secondsUntilRunFirstRun * 1000.0);
timer.AutoReset = false;
timer.Elapsed += (sender, e) =>
{
    Console.WriteLine(DateTime.Now.ToString("hh:mm:ss.fff"));

    timer.Interval = (secondsInterval - (DateTime.Now.TimeOfDay.TotalSeconds % secondsInterval)) * 1000.0;
};
timer.Start();


06:51:45.009
06:51:50.001
...
06:52:50.011
06:52:55.013
06:53:00.001

最佳答案

不要让计时器的误差累积。使用 RTC 计算超时时间之前剩余的毫秒数。这次将 Sleep/setInterval 减半。当计时器触发/ sleep 返回时,再次使用 RTC 重新计算剩余的间隔并将间隔/ sleep 再次设置为半衰期。重复此循环,直到剩余间隔小于 50ms。然后 CPU 在 RTC 上循环,直到超过所需时间。触发事件。

Rgds, 马丁

关于c# - System.Threading.Timer 调用每天漂移几秒钟,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/6259120/

相关文章:

计算 C 的时间差(天、小时、分钟、秒)

c++ - boost::chrono::time_point<> 和 boost::chrono::steady_clock::time_point 之间的区别

c# - 从 Windows 服务创建文件/文件夹时的最佳实践

delphi - 在delphi中创建Windows服务

c# - 对用户没有可见形式的桌面通知程序

c# - 将一个字符串拆分成多个字符串

c# - 从文件中获取文本 C#

c# - 有序列表的减法

python - time.time() 不计时

c# - 我必须在 Windows 服务中实现 Stop 方法吗?