У меня есть служба, которая всегда работает, у нее есть таймер для выполнения определенного действия каждый день в 2 часа ночи.
TimeSpan runTime = new TimeSpan(2, 0, 0); // 2 AM
TimeSpan timeToFirstRun = runTime - DateTime.Now.TimeOfDay;
if (timeToFirstRun.TotalHours < 0)
{
timeToFirstRun += TimeSpan.FromDays(1.0);
}
_dailyNodalRunTimer = new Timer(
RunNodalDailyBatch,
null,
timeToFirstRun,
TimeSpan.FromDays(1.0)); //repeat event daily
Этот код инициализации вызывается один раз при первом запуске службы, за последние несколько дней я вошел в систему при срабатывании таймера:
2011-05-21 02:00:01.580
2011-05-22 02:00:03.840
...
2011-05-31 02:00:25.227
2011-06-01 02:00:27.423
2011-06-02 02:00:29.847
Как вы можете видеть, как он дрейфует на 2 секунды каждый день, все дальше и дальше от того момента, когда он должен был выстрелить (в 2 часа ночи).
Я неправильно его использую или этот таймер не предназначен для точной работы? Я мог воссоздавать таймер каждый день или запускать его с небольшим интервалом и многократно проверять, хочу ли я выполнить действие, но это выглядит довольно странно.
EDIT
Я попытался использовать System.Timers.Timer, и, похоже, проблема та же. Сброс интервала происходит потому, что вы не можете запланировать начальное время до первого тика в System.Timers.Timer, как в System.Threading.Timer
.
int secondsInterval = 5;
double secondsUntilRunFirstRun = secondsInterval - (DateTime.Now.TimeOfDay.TotalSeconds % secondsInterval);
var timer = new System.Timers.Timer(secondsUntilRunFirstRun * 1000.0);
timer.AutoReset = true;
timer.Elapsed += (sender, e) =>
{
Console.WriteLine(DateTime.Now.ToString("hh:mm:ss.fff"));
if (timer.Interval != (secondsInterval * 1000.0))
timer.Interval = secondsInterval * 1000.0;
};
timer.Start();
Произведите следующие моменты, вы можете увидеть, как они слегка дрейфуют:
06:47:40.020
06:47:45.035
06:47:50.051
...
06:49:40.215
06:49:45.223
06:49:50.232
Так что я полагаю, что лучший подход - это просто перенести таймер в обработчик тиков? Следующее производит тик с регулярным интервалом в течение ~ 15 миллисекунд
double secondsUntilRunFirstRun = secondsInterval - (DateTime.Now.TimeOfDay.TotalSeconds % secondsInterval);
var timer = new System.Timers.Timer(secondsUntilRunFirstRun * 1000.0);
timer.AutoReset = false;
timer.Elapsed += (sender, e) =>
{
Console.WriteLine(DateTime.Now.ToString("hh:mm:ss.fff"));
timer.Interval = (secondsInterval - (DateTime.Now.TimeOfDay.TotalSeconds % secondsInterval)) * 1000.0;
};
timer.Start();
06:51:45.009
06:51:50.001
...
06:52:50.011
06:52:55.013
06:53:00.001