TimeSpan imprecision in Windows Service

59 Views Asked by At

I developed a windows service that runs a task from time to time. However, the task execution interval is every hour. However, I am noticing that the task is being executed approximately every 1 hour and 5 seconds, and this is a problem.

I understand that Windows is not a real-time operating system, but don't you think the inaccuracy is too great, adding 5 seconds to the set interval?

I try to use TimeSpan.FromMinutes(60) and TimeSpan.FromSeconds(3600) and I have the same result.

I need to use System.Threading.Timer because only this class has the possibility of applying a delay to first execution.

Basically I use:

_timer = new Timer(DestFunction, null, TimeSpan.FromSeconds(delaysec), TimeSpan.FromSeconds(3600));

What can I do, to keep the execution in same second every hour?

2

There are 2 best solutions below

0
Diseño Informático On

Try saving a log file with timestamp. Maybe those 5 seconds are delaysec or the service time to start.

1
Olivier Jacot-Descombes On

This has nothing to do with the precision of TimeSpan. It is due to how the operating system allocates runtime to the service and the time it takes for the service to wake up, etc.

I suggest to calculate the delay from the next planned execution date/time and the current date/time. Like this, these unwanted delays will not sum up.

DateTime next = DateTime.Now + TimeSpan.FromHours(1);

// Truncate minutes and seconds. I.e., make it a full hour.
next = new DateTime(next.Year, next.Month, next.Day, next.Hour, 0, 0);
TimeSpan delay = next - DateTime.Now;

Of course, you could add minutes to the calculated next, if you don't want to execute the task at the full hour. e.g. at 15 minutes past every hour:

next = new DateTime(next.Year, next.Month, next.Day, next.Hour, 15, 0);

If this is still not precise enough, you could shorten the delay by a few seconds and then loop until the expected time.

DateTime next = DateTime.Now + TimeSpan.FromHours(1);
next = new DateTime(next.Year, next.Month, next.Day, next.Hour, 0, 0);
TimeSpan delay = next - DateTime.Now - TimeSpan.FromSeconds(10);

//TODO: Start your timer or schedule your task somehow.

When the timer event fires or the task resumes:

while (DateTime.Now < next) {
    Thread.Sleep(100);
}
// TODO execute the task