Reputation: 2449
I am currently working on a time sensitive program, where is is important that i do a reading from a device every second. I am currently using a Timer for this, i have however found that the time steady increases the interval with 1 ms pr tick. This can cause a problem as the program will run for a long time.
Read Exe Time
is how long the code took to execute.
Performing reading
is at start each elapsed event.
How can i ensure that the clock will not increase, i can understand that the tick might not be on the exact millisecond, but that i keep climbing seems weird. and my code do not take the full 1000 ms to execute, so that is not it. After 16.6 minutes will the readings have been pushed a whole second.
Read Exe Time: 574.0637 ms
Performing reading: 22:58:39.696
Read Exe Time: 571.9422 ms
Performing reading: 22:58:40.697
Read Exe Time: 595.5333 ms
Performing reading: 22:58:41.697
Read Exe Time: 566.2602 ms
Performing reading: 22:58:42.698
Read Exe Time: 568.2275 ms
Performing reading: 22:58:43.698
Read Exe Time: 569.7573 ms
Performing reading: 22:58:44.700
Read Exe Time: 561.655 ms
Performing reading: 22:58:45.701
Read Exe Time: 567.8385 ms
Performing reading: 22:58:46.702
Read Exe Time: 584.8305 ms
Performing reading: 22:58:47.703
Read Exe Time: 588.754 ms
Performing reading: 22:58:48.703
Read Exe Time: 560.8154 ms
Performing reading: 22:58:49.704
Read Exe Time: 567.9324 ms
Performing reading: 22:58:50.705
Read Exe Time: 579.1354 ms
Performing reading: 22:58:51.706
Read Exe Time: 563.0227 ms
Performing reading: 22:58:52.707
Read Exe Time: 569.557 ms
Performing reading: 22:58:53.708
Read Exe Time: 560.707 ms
Performing reading: 22:58:54.708
Read Exe Time: 574.6268 ms
Performing reading: 22:58:55.709
Read Exe Time: 570.4872 ms
Performing reading: 22:58:56.710
Read Exe Time: 574.8388 ms
Performing reading: 22:58:57.710
Read Exe Time: 573.2054 ms
Performing reading: 22:58:58.710
Read Exe Time: 578.6189 ms
Performing reading: 22:58:59.711
Read Exe Time: 565.7442 ms
Performing reading: 22:59:0.711
Read Exe Time: 564.7523 ms
Performing reading: 22:59:1.712
Read Exe Time: 575.8134 ms
Performing reading: 22:59:2.713
Read Exe Time: 570.9416 ms
Performing reading: 22:59:3.713
Read Exe Time: 573.1493 ms
Performing reading: 22:59:4.714
Read Exe Time: 570.2831 ms
Performing reading: 22:59:5.714
Read Exe Time: 568.2672 ms
Performing reading: 22:59:6.715
Read Exe Time: 586.5607 ms
Performing reading: 22:59:7.715
Read Exe Time: 588.0465 ms
Performing reading: 22:59:8.715
Read Exe Time: 574.1118 ms
Performing reading: 22:59:9.716
The code
private void StartMeter()
{
this.Meter.Start();
this.ReadingTime = new Timer(1000);
this.ReadingTime.Elapsed += new ElapsedEventHandler(PerformReading);
this.ReadingTime.Start();
}
private void PerformReading(object sender, ElapsedEventArgs e)
{
Console.WriteLine("Performing reading: " + DateTime.Now.Hour + ":" + DateTime.Now.Minute + ":" + DateTime.Now.Second + "." + DateTime.Now.Millisecond);
// Code here
}
Update: If i just set the time to 999 ms will it steadily decrease instead
Performing reading: 23:4:50.527
Read Exe Time: 562.7729 ms
Performing reading: 23:4:51.527
Read Exe Time: 566.8178 ms
Performing reading: 23:4:52.527
Read Exe Time: 562.3829 ms
Performing reading: 23:4:53.526
Read Exe Time: 567.9165 ms
Performing reading: 23:4:54.526
Read Exe Time: 561.1329 ms
Performing reading: 23:4:55.525
Read Exe Time: 562.9359 ms
Performing reading: 23:4:56.525
Read Exe Time: 560.4151 ms
Performing reading: 23:4:57.524
Read Exe Time: 561.0302 ms
Performing reading: 23:4:58.524
Read Exe Time: 561.5756 ms
Performing reading: 23:4:59.524
Read Exe Time: 565.2936 ms
Performing reading: 23:5:0.523
Read Exe Time: 561.8903 ms
Performing reading: 23:5:1.523
Read Exe Time: 561.8768 ms
Performing reading: 23:5:2.523
Read Exe Time: 562.3904 ms
Performing reading: 23:5:3.523
Read Exe Time: 562.3363 ms
Performing reading: 23:5:4.523
Read Exe Time: 561.6288 ms
Performing reading: 23:5:5.523
Read Exe Time: 560.4596 ms
Performing reading: 23:5:6.522
Read Exe Time: 562.34 ms
Performing reading: 23:5:7.522
Read Exe Time: 561.5994 ms
Performing reading: 23:5:8.522
Read Exe Time: 561.6811 ms
Performing reading: 23:5:9.521
Read Exe Time: 561.7427 ms
Performing reading: 23:5:10.521
Read Exe Time: 561.8044 ms
Performing reading: 23:5:11.520
Read Exe Time: 561.6246 ms
Performing reading: 23:5:12.520
Read Exe Time: 560.5753 ms
Performing reading: 23:5:13.520
Read Exe Time: 563.8604 ms
Performing reading: 23:5:14.520
Read Exe Time: 562.2606 ms
Performing reading: 23:5:15.534
Read Exe Time: 560.8377 ms
Performing reading: 23:5:16.534
Read Exe Time: 560.4553 ms
Performing reading: 23:5:17.534
Read Exe Time: 562.5534 ms
Performing reading: 23:5:18.533
Read Exe Time: 563.0269 ms
Performing reading: 23:5:19.532
Read Exe Time: 561.2851 ms
Performing reading: 23:5:20.532
Read Exe Time: 560.3442 ms
Performing reading: 23:5:21.531
Read Exe Time: 561.5201 ms
Performing reading: 23:5:22.530
Read Exe Time: 560.609 ms
Performing reading: 23:5:23.530
Edit: For @PeterLuu
Performing reading: 22:44:45.13
Performing reading: 22:44:45.449
Performing reading: 22:44:45.879
Performing reading: 22:44:46.320
Performing reading: 22:44:46.761
Performing reading: 22:44:47.192
Performing reading: 22:44:47.631
Performing reading: 22:44:48.74
Performing reading: 22:44:48.577
Performing reading: 22:44:49.49
Performing reading: 22:44:49.637
And the code
private void StartMeter()
{
DateTime now = DateTime.UtcNow;
NextTickTimeWholeSeconds = new DateTime(now.Ticks - (now.Ticks % TimeSpan.TicksPerSecond), now.Kind);
this.Meter.Start();
this.ReadingTime = new Timer(1000);
this.ReadingTime.Elapsed += new ElapsedEventHandler(PerformReading);
this.ReadingTime.Start();
ReadingTime.Interval = GetTimeToNextSecond();
}
private double GetTimeToNextSecond()
{
NextTickTimeWholeSeconds = NextTickTimeWholeSeconds.AddSeconds(1);
var interval = NextTickTimeWholeSeconds - DateTime.UtcNow;
return interval.Milliseconds < 1 ? GetTimeToNextSecond() : interval.Milliseconds;
}
private void PerformReading(object sender, ElapsedEventArgs e)
{
Console.WriteLine("Performing reading: " + DateTime.UtcNow.Hour + ":" + DateTime.UtcNow.Minute + ":" + DateTime.UtcNow.Second + "." + DateTime.UtcNow.Millisecond);
// My code takes about 500-600 ms
ReadingTime.Interval = GetTimeToNextSecond();
}
Upvotes: 4
Views: 2522
Reputation: 446
You can base it off of the time until the next second (e.g. if there is only 890 milliseconds until the next second on the dot) and restart the timer with that interval every iteration to prevent drifting. Adapted from a previous answer by Jared here. This does not offer precision to the exact millisecond, but it will prevent your times from drifting and the event will always fire "on the dot".
Edit: Removed an unnecessary line - You actually don't need to call Start() twice, just changing the interval is sufficient since changing the interval restarts the timer.
Edit 2: Made some changes to make it more accurate in edge cases (e.g. prevent firing multiple times in a row).
public class Meter
{
private Timer ReadingTime;
private DateTime NextTickTimeWholeSeconds;
public Meter() {
DateTime now = DateTime.UtcNow;
NextTickTimeWholeSeconds = new DateTime(now.Ticks - (now.Ticks % TimeSpan.TicksPerSecond), now.Kind);
ReadingTime = new Timer();
ReadingTime.AutoReset = false;
ReadingTime.Elapsed += new ElapsedEventHandler(PerformReading);
ReadingTime.Interval = GetTimeToNextSecond();
}
public void StartMeter()
{
ReadingTime.Start();
}
private double GetTimeToNextSecond()
{
NextTickTimeWholeSeconds = NextTickTimeWholeSeconds.AddSeconds(1);
var interval = NextTickTimeWholeSeconds - DateTime.UtcNow;
return interval.Milliseconds < 1 ? GetTimeToNextSecond() : interval.Milliseconds;
}
private void PerformReading(object sender, ElapsedEventArgs e)
{
Console.WriteLine("Performing reading: " + DateTime.Now.Hour + ":" + DateTime.Now.Minute + ":" + DateTime.Now.Second + "." + DateTime.Now.Millisecond);
ReadingTime.Interval = GetTimeToNextSecond();
}
}
Example Output:
Performing reading: 18:11:47.10
Performing reading: 18:11:48.4
Performing reading: 18:11:49.10
Performing reading: 18:11:50.6
Performing reading: 18:11:51.9
Performing reading: 18:11:52.5
Performing reading: 18:11:53.10
Performing reading: 18:11:54.5
Performing reading: 18:11:55.9
Performing reading: 18:11:56.7
Performing reading: 18:11:57.7
Performing reading: 18:11:58.7
Performing reading: 18:11:59.7
Performing reading: 18:12:0.8
Performing reading: 18:12:1.7
Performing reading: 18:12:2.6
... about 50 seconds later ...
Performing reading: 18:12:50.1
Performing reading: 18:12:51.0
Performing reading: 18:12:52.0
Performing reading: 18:12:53.0
Performing reading: 18:12:53.999
Performing reading: 18:12:55.0
Performing reading: 18:12:56.0
Performing reading: 18:12:56.999
Performing reading: 18:12:58.0
Performing reading: 18:12:59.0
Performing reading: 18:13:0.0
Performing reading: 18:13:0.999
Performing reading: 18:13:2.0
Performing reading: 18:13:3.1
Performing reading: 18:13:4.0
Performing reading: 18:13:5.0
Performing reading: 18:13:6.0
Performing reading: 18:13:6.999
Performing reading: 18:13:8.0
Performing reading: 18:13:9.0
Performing reading: 18:13:10.0
Performing reading: 18:13:11.0
Performing reading: 18:13:12.1
Performing reading: 18:13:13.0
Performing reading: 18:13:13.999
Performing reading: 18:13:15.0
Performing reading: 18:13:16.0
Performing reading: 18:13:16.999
Performing reading: 18:13:18.0
Performing reading: 18:13:19.1
Performing reading: 18:13:20.0
Performing reading: 18:13:21.0
Performing reading: 18:13:21.999
Performing reading: 18:13:23.0
Performing reading: 18:13:24.0
Performing reading: 18:13:25.0
Performing reading: 18:13:26.0
Performing reading: 18:13:27.0
Performing reading: 18:13:28.0
Performing reading: 18:13:29.0
Upvotes: 3
Reputation: 2620
Using CodingBarfield's response, worked very well for me some time ago.
Raise event in high resolution interval/timer
Sorry for not giving more details, is nicely explained there.
Upvotes: 1