I am trying to fire a method at precisely each minute.
I am using the code below, but I notice that the timer can fire several milliseconds early.
How might I fix that?
public partial class Form1 : Form
{
DateTime RoundUpFuzzy(DateTime dt, TimeSpan d)
{
int n=1;
if (dt.Millisecond > 900)
n = 2;
return new DateTime(((dt.Ticks + (d.Ticks*n) - 1) / d.Ticks) * d.Ticks);
}
public void HistMatch()
{
for (int i = 0; i < 10; i++)
{
var now = DateTime.UtcNow;
var nextDT = RoundUpFuzzy(now, TimeSpan.FromMinutes(1));
var waitTS = nextDT.Subtract(now);
Console.WriteLine("Now: " + now.ToString("HH:mm:ss.fff") + " nextDT: " + nextDT.ToString("HH:mm:ss.fff")+" wait MS: " + waitTS.TotalMilliseconds);
System.Threading.Thread.Sleep(waitTS);
}
}
}
Output:
Now: 09:12:16.308 nextDT: 09:13:00.000 waitMS: 43691.7256
Now: 09:12:59.999 nextDT: 09:14:00.000 waitMS: 60000.0952
Now: 09:13:59.990 nextDT: 09:15:00.000 waitMS: 60009.0949
Now: 09:14:59.992 nextDT: 09:16:00.000 waitMS: 60007.4955
Now: 09:15:59.993 nextDT: 09:17:00.000 waitMS: 60006.2285
Now: 09:16:59.996 nextDT: 09:18:00.000 waitMS: 60003.2285
Now: 09:17:59.996 nextDT: 09:19:00.000 waitMS: 60003.2285
Now: 09:18:59.996 nextDT: 09:20:00.000 waitMS: 60003.2285
EDIT 1 - Additional Test code
Here I am trying 3 versions to run an event exactly each minute. Only the first "Test_Sleep" is accurate. How can I get the same result but using a timer?
public partial class Form1 : Form
{
System.Threading.Timer threadingTimer;
System.Timers.Timer timersTimer;
public Form1()
{
InitializeComponent();
}
private void button1_Click(object sender, EventArgs e)
{
Test_Sleep();
Test_ThreadingTimer("");
Test_TimersTimer("", null);
}
public void Test_TimersTimer(object o, System.Timers.ElapsedEventArgs e)
{
var now = DateTime.UtcNow;
var nextDT = RoundUpFuzzy(now, TimeSpan.FromMinutes(1));
var ms = nextDT.Subtract(now);
Console.WriteLine("Now: " + now.ToString("HH:mm:ss.fff") + " nextDT: " + nextDT.ToString("HH:mm:ss.fff") + " waitMS: " + ms.TotalMilliseconds);
timersTimer = new System.Timers.Timer();
timersTimer.Interval = (int)ms.TotalMilliseconds;
timersTimer.Elapsed += Test_TimersTimer;
timersTimer.AutoReset = false;
timersTimer.Enabled = true;
}
public void Test_ThreadingTimer(object o)
{
var now = DateTime.UtcNow;
var nextDT = RoundUpFuzzy(now, TimeSpan.FromMinutes(1));
var ms = nextDT.Subtract(now);
Console.WriteLine("Now: " + now.ToString("HH:mm:ss.fff") + " nextDT: " + nextDT.ToString("HH:mm:ss.fff") + " waitMS: " + ms.TotalMilliseconds);
threadingTimer = new System.Threading.Timer(new System.Threading.TimerCallback(Test_ThreadingTimer), null, ms, TimeSpan.FromMilliseconds(-1));
}
public void Test_Sleep()
{
for (int i = 0; i < 10; i++)
{
var now = DateTime.UtcNow;
var nextDT = RoundUpFuzzy(now, TimeSpan.FromMinutes(1));
var waitTS = nextDT.Subtract(now);
Console.WriteLine("Now: " + now.ToString("HH:mm:ss.fff") + " nextDT: " + nextDT.ToString("HH:mm:ss.fff") + " waitMS: " + waitTS.TotalMilliseconds);
System.Threading.Thread.Sleep(waitTS);
}
}
DateTime RoundUpFuzzy(DateTime dt, TimeSpan d)
{
int n = 1;
if (dt.Millisecond > 900)
n = 2;
return new DateTime(((dt.Ticks + (d.Ticks * n) - 1) / d.Ticks) * d.Ticks);
}
}