简体   繁体   English

C#.NET 2 Threading.Timer - 时间漂移

[英]C# .NET 2 Threading.Timer - time drifting

I have a dll consumed by a service. 我有一个服务消耗的DLL。 Its basic job is to run every X minutes and perform some system checks. 它的基本工作是每隔X分钟运行一次并执行一些系统检查。 In my dll I have a top level class that declares a System.threading.timer and a Timercallback. 在我的DLL中,我有一个顶级类,声明了System.threading.timer和Timercallback。 The constructor for the class initialises the timerCallback with my thread function. 该类的构造函数使用我的线程函数初始化timerCallback。 In my "Onstart" handler I initialise the timer with the timercallback and set the next time to fire and interval time. 在我的“Onstart”处理程序中,我使用timercallback初始化计时器,并设置下次触发和间隔时间。 In my case its every 10 minutes. 在我的情况下,它每10分钟。 Usually in these 10 minute checks there is nothing to do but the service is forced to do something at least once every day at a set time. 通常在这10分钟的检查中,没有什么可做的,但服务被迫在设定的时间每天至少做一次。

My problem: I am finding that during testing, the time the daily check is carried out every day is slowly drifitng away from the desired start time of 8.30. 我的问题:我发现在测试过程中,每天进行每日检查的时间会慢慢偏离8.30的预期开始时间。 eg over about 20 odd days my time has drifted from 08.30 to 08.31.35. 例如,大约20多天,我的时间从08:30升至08.31.35。 It drifts about 4 - 6 seconds every day. 它每天漂移大约4-6秒。

My question: does anyone know why the time is drifting like this and how can I make it stick to its allotted time? 我的问题:有没有人知道为什么时间漂流这样,我怎么能坚持它的分配时间?

thanks 谢谢

The time "drifts" because the timer is simply not that precise. 时间“漂移”,因为计时器根本就不那么精确。 If you need to run your code as closely as possible to a certain interval, you can do something like this: 如果您需要尽可能密切地运行代码到某个时间间隔,您可以执行以下操作:

public void MyTimerCallback(object something) {
    var now = DateTime.UtcNow;
    var shouldProbablyHaveRun = new DateTime(
        now.Year, now.Month, now.Day,
        now.Hour, now.Minute - (now.Minute % 10), 0);
    var nextRun = shouldProbablyHaveRun.AddMinutes(10.0);

    // Do stuff here!

    var diff = nextRun - DateTime.UtcNow;
    timer.Change(diff, new TimeSpan(-1));
}

...assuming you are using a System.Threading.Timer instance. ...假设您正在使用System.Threading.Timer实例。 Modify the example if you are using any other type of timer (there are several!) . 如果您正在使用任何其他类型的计时器(有几个!),请修改该示例。

Why not check every minute if the action needs to be performed? 如果需要执行操作,为什么不检查每一分钟?

ie: 即:

if (DateTime.Now.Minute % 10) == 0 

it takes a finite amount of time to do the operations you are doing in your timer method handler, so it makes sense that it's not going to happen every 10 minutes to the second, especially if you are scheduling the next wakeup after doing your checks and such. 在你的计时器方法处理程序中执行你正在进行的操作需要花费有限的时间,因此有意义的是它不会每隔10分钟发生一次,特别是如果你在进行检查后安排下次唤醒这样。 if you are already checking anyway for answering is it time to do this, you should make your timer fire more frequently to satisfy the resolution you need and trust your check of when it should execute something to make sure that it does. 如果你已经在检查是否有时间进行回答是时候这样做,你应该让你的计时器更频繁地开火以满足你需要的分辨率,并相信你应该检查何时应该执行某些事情以确保它能够做到。 you probably need some sort of persistence to make sure it doesn't execute twice (if that is important) in case there is a shut down/restart and the state of knowing whether it has already run is not still in memory. 您可能需要某种持久性以确保它不会执行两次(如果这很重要),以防关闭/重新启动并且知道它是否已经运行的状态仍然不在内存中。

Here is my take: 这是我的看法:

while ((DateTime.Now - lastRunTime).TotalSeconds < 600)
     CurrentThread.Sleep(1000);

or just register a windows timer and execute in response to the event/callback 或者只是注册一个Windows计时器并执行以响应事件/回调

public static void Main()
{
    System.Timers.Timer aTimer = new System.Timers.Timer();
    aTimer.Elapsed+=new ElapsedEventHandler(OnTimedEvent);
    // Set the Interval to 600 seconds.
    aTimer.Interval=600000;
    aTimer.Enabled=true;

    Console.WriteLine("Press \'q\' to quit the sample.");
    while(Console.Read()!='q');
}

// Specify what you want to happen when the Elapsed event is raised.
private static void OnTimedEvent(object source, ElapsedEventArgs e)
{
    Console.WriteLine("10 minutes passed!");
}

Timers aren't exact, just approximate. 定时器不精确,只是近似。 Don't use the logic of "just add 10 minutes". 不要使用“只需添加10分钟”的逻辑。 Each time your timer fires, you need to check for time skew and adjust. 每次计时器触发时,都需要检查时间偏差并进行调整。

eg. 例如。 If you say "wake me up in 10min", and it wakes you up in 10min 1sec, then the next timer needs to be 9min 59sec, not 10min. 如果你说“在10分钟内叫醒我”,它会在10分钟1秒内唤醒你,那么下一个计时器需要9分59秒,而不是10分钟。

Also, you want to assign your next timer at the end of your logic. 此外,您希望在逻辑结束时分配下一个计时器。

eg. 例如。 say you want to start taskA every 10min and it takes 2 seconds to run. 假设您想每10分钟启动一次任务,并且需要2秒才能运行。 Your timer starts and 10 minutes later it wakes up to run taskA. 你的计时器启动,10分钟后它就会启动以运行taskA。 It kicks off, finishes, now you add 10 minutes. 它开始,结束,现在你加10分钟。 But it took 2 seconds to run your task. 但是花了2秒钟来完成你的任务。 So 10 minutes from the time your code ran will be skewed by 2 seconds. 因此,距您的代码运行10分钟将会偏差2秒。

What you need to do is predict the next time you need to run and find the difference between now and then and set the timer to that difference. 您需要做的是预测下次需要运行并找到now和then之间的差异,并将计时器设置为该差异。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM