Объяснение Тимоти убедительно, но System.Threading.Timer уже работает таким образом. По крайней мере, в реализации Rotor он использует значение, возвращаемое GetTickCount (), чтобы вычислить время следующего обратного вызова. Маловероятно, что реальный CLR также делает это, он с большей вероятностью использует CreateTimerQueueTimer (). Тем не менее, эта функция API также, вероятно, будет зависеть от внутреннего тика, увеличенного прерыванием такта.
Вопрос в том, насколько хорошо тик внутренних часов (возвращаемый GetTickCount () и Environment.TickCount) синхронизируется с абсолютным временем стены (возвращаемым DateTime.Now). К сожалению, это деталь реализации HAL на вашем компьютере, уровень абстрагирования оборудования. Изготовитель машины может предоставить специальный HAL, который был настроен для работы с BIOS и чипсетом машины.
Я бы ожидал проблем, если бы у машины было два источника синхронизации, микросхема тактовой частоты, которая была разработана для слежения за временем стены и тикала даже при отключении питания машины, и другая частота, доступная на чипсете, которую можно разделить для часы прерывают Оригинальный IBM AT PC работал именно так. Чип тактовой частоты обычно можно доверять, если часы не будут точными, часы будут сильно отключены. Чипсет не может, сокращение углов качества генераторов - это простой способ сэкономить копейки.
Решите вашу проблему, избегая длительных периодов времени в таймере и пересчитывая следующий срок выполнения из DateTime.Now в обратном вызове.