0

I want to write a program which needs to get a tick event each second. The program runs many hours and the tick must be in line with the seconds of the system clock. To use any kind of timer which fires an event each second is no good idea. There are to many inaccuracies. If you have an inaccuracy of 100ms per second, the timer is 6 minus behind the system clock each hour. But, it must be exact in line with the system clock.

To get an event from the system clock is ok.

To have inaccuracies is ok for single seconds but not over all.

What is your suggestion to solve this?

The solution must work on c# net6 (I plan to run the program on Windows and Linux).

Thanks

  • 2
    What did you try ? – Steve B Jan 06 '22 at 14:36
  • Reading material [Most accurate timer in .NET?](https://stackoverflow.com/questions/9228313/most-accurate-timer-in-net) – Steve Jan 06 '22 at 14:37
  • One way to handle this is to set a much shorter interval for the timer and then use the absolute time (DateTime.Now) to see if it's time to increment your second count. – adv12 Jan 06 '22 at 14:50
  • 1
    In previous versions of the software I used the Winmm.dll solution. This was very exact and working. The new version must be able to run Linux. So, Winmm.dll is no solution anymore. – Jürgen Schneiderat Jan 06 '22 at 15:09
  • Have your confirmed that a timer firing every second will be inaccurate? I don't think it will be. It's easy to test. – Scott Hannen Jan 06 '22 at 16:26

2 Answers2

1

This gets you to within 0..20ms after each whole second of the clock.

static System.Threading.Timer timer = new(CallBack, null, 0, 0);

static void CallBack(object? state)
{
  var now = DateTime.Now;
  timer.Change(1000 - now.Millisecond, 0);

  // Console.WriteLine(now + " " + now.Millisecond);
}
H H
  • 263,252
  • 30
  • 330
  • 514
0

If accuracy is an issue and you don't like the accuracy of a timer that raises events, then a simple workaround is to use a polling method.

I'm not saying this is efficient, and you would want to ensure that the task that you execute for each tick completes in less time than the unit you want to fire on, so in this case a second, but it satisfies the requirement of being tightly bound to the system clock:

Action tickAction = () => Console.WriteLine(DateTime.Now);
int lastTick = DateTime.Now.TimeOfDay.Seconds;
while (true)
{
    var seconds = DateTime.Now.TimeOfDay.Seconds;
    if(lastTick != seconds)
    {
        lastTick = seconds;
        tickAction();
    }
}

You could also after each action start a timer with a delay equal to the number of ticks remaining by the next second, but that because a bit overkill for a 1 second timer. If however the time interval was variable, or the time the action takes to execute might exceed the next window, then it might make enough sense to do it in a timer based way.

Overall the event processing loop and whatever else is going on in the background can affect the accuracy even of this solution, having more context on the action that you want to execute on each second would be helpful to produce a more commercially viable solution.

Chris Schaller
  • 13,704
  • 3
  • 43
  • 81
  • This is more or less what I have already tried. setup a timer each second and do this: keep the aktual time after the tick, take the new time and calculate the duration Check if the duration is 1 second But this is not really stable and I think there must be a better way. Best would be to get each second tick from the system clock. – Jürgen Schneiderat Jan 06 '22 at 15:17
  • If you are just rendering a clock, then a perpetual timer with 20ms delay (or less) that updates the visual will keep things visually in sync with the actual system clock. It doesn'tr really matter that 50 times a second the visual is updated with the same content, its just a clock. – Chris Schaller Jan 06 '22 at 15:22