Подтвердить что ты не робот

Как работает System.Timers.Timer в приложении WPF, после спящего режима и сна?

Я использую System.Timers.Timer в своем приложении WPF. Я хочу понять, как работает Таймер, после того, как компьютер находится в спящем режиме, и спать. Я получаю некоторые странные проблемы с моим приложением, после того как компьютер возобновляется из спящего режима.

Как мне обращаться с таймерами и как они себя ведут, когда компьютер находится в режиме спящего режима/спящего режима?

У меня есть полуночный таймер, который должен работать каждую полуночью до reset значений по умолчанию в пользовательском интерфейсе.

Вот код, который создает таймер:

private void ResetMidnightTimer() 
        { 
            // kill the old timer
            DisposeMidnightTimer();

            _midnightTimer = new Timer();
            // scheduling the timer to elapse 1 minute after midnight
            _midnightTimer.Interval = (DateTime.Today.AddDays(1).AddMinutes(1) - DateTime.Now).TotalMilliseconds;
            _midnightTimer.Elapsed += (_, __) => UpdateRecommendedCollectingTime();
            _midnightTimer.Enabled = true;
            _midnightTimer.Start();
        }

В редакторе страниц пользовательского интерфейса я вызываю метод, который вызывает ResestMidnightTimer() и создает де-факто таймер. После этого таймер просто ждет ночи.

Когда наступает ночное время (на самом деле это 12:01 AM), таймер работает, сбрасывает значения по умолчанию, как ожидалось, а затем удаляет существующий таймер. Наконец, он создает новый полуночный таймер на следующий день. Но если я попытаюсь спящий компьютер в течение этого дня, полуночный таймер не будет работать и не будет reset значения по умолчанию.

Является ли это потому, что в то время как спящий режим просто откладывает обработку события на такое же количество времени, что и спящий?

4b9b3361

Ответ 1

Это зависит от того, как вы используете таймеры. Если вы используете их для инициирования какого-либо события, которое происходит нечасто (больше, чем через пару минут), вы, вероятно, увидите какое-то "странное" поведение. Поскольку вы не указываете, что такое "странное" поведение, я собираюсь предположить, что таймер вашей программы отключается позже, чем он должен.

Объяснение: Проблема с переходом в спящий режим/спящий режим заключается в том, что все программы приостановлены. Это означает, что ваши таймеры не обновляются и, таким образом, когда вы спите/спящий режим и возвращаетесь назад, это похоже на то, что вы были заморожены в течение этого периода времени, когда вы спали/спячки. Это означает, что если у вас есть таймер, установленный на выключение через час, и ваш компьютер перейдет спать с отметкой в ​​15 минут, после того, как он просыпается, у него будет еще 45 минут, независимо от того, как долго спал компьютер.

Решение.. Одним из исправлений будет сохранение DateTime в последний раз, когда произошло событие. Затем таймер уходит периодически (каждые 10 секунд или 10 минут, в зависимости от желаемой точности) и проверяет DateTime последнего исполнения. Если разница между текущим и последним временем выполнения больше или равна требуемому интервалу, THEN вы выполняете выполнение.

Это исправит его так, что если во время спящего режима/спящего режима произойдет событие "должно быть", оно начнется с момента возвращения из спящего режима/спящего режима.

Обновление: Решение, представленное выше, будет работать, и я напишу несколько деталей, которые помогут вам его реализовать.

  • Вместо того, чтобы создавать/удалять новые таймеры, создайте таймер ОДИН, чтобы использовать RECURRING (для свойства AutoReset установлено значение true)

  • Интервал одиночного таймера должен быть НЕ установлен в соответствии со следующим событием. Вместо этого он должен быть установлен на выбранное вами значение, которое будет представлять частоту опроса (как часто он проверяет, должно ли выполняться "событие" ). Выбор должен быть сбалансирован с эффективностью и точностью. Если вам НУЖНО, чтобы он выполнял ДЕЙСТВИТЕЛЬНО близко к 12:01, то вы устанавливаете интервал примерно на 5-10 секунд. Если менее важно, чтобы это было ровно в 12:01, вы можете увеличить интервал примерно на 1-10 минут.

  • Вам нужно сохранить значение DateTime, когда последнее выполнение произошло ИЛИ, когда произойдет следующее выполнение. Я предпочел бы "когда следующее выполнение должно произойти", чтобы вы не делали (LastExecutionTime + EventInterval) каждый раз, когда таймер истекает, вы просто будете сравнивать текущее время и время, в которое должно произойти событие.

  • Как только таймер истечет, и произойдет событие СЛЕДУЕТ (где-то около 12:01 утра), вы должны обновить сохраненное DateTime, а затем запустить код, который вы хотите запустить в 12:01.

Спящий и гибернатный пояснения.. Основное различие между сном и спящим - это то, что во сне все хранится в ОЗУ, а спящий режим сохраняет текущее состояние на диск. Основным преимуществом спящего режима является то, что ОЗУ больше не нуждается в мощности и, следовательно, потребляет меньше энергии. Вот почему рекомендуется использовать спящий режим во сне при работе с ноутбуками или другими устройствами с использованием конечного количества энергии.

Тем не менее, нет разницы в исполнении программ, поскольку они приостанавливаются в любом случае. К сожалению, System.Timers.Timer не "просыпается" на компьютере, поэтому вы не можете обеспечить, чтобы ваш код запускался в ~ 12: 01 AM.

Я считаю, что есть другие способы "просыпаться" на компьютере, но если вы не идете по этому маршруту, вы можете просто запустить свое "событие" во время следующего "опроса" вашего таймера после того, как он выйдет из сна/спящий режим.

Ответ 2

Это потому, что в то время как спящий режим просто откладывает обработку события за такое же количество времени, что он был спящий?

Пока компьютер находится в режиме ожидания (т.е. спящий или спящий режим), он ничего не делает. Это включает в себя, в частности, планировщик, который обрабатывает пробуждение потока, который контролирует очередь событий таймера, не работает и поэтому поток не продвигается к возобновлению выполнения для обработки следующего таймера.

Это не так много, что событие явно откладывается как таковое. Но да, это чистый эффект.

В некоторых случаях можно использовать класс таймера, который не имеет этой проблемы. Оба System.Windows.Forms.Timer и System.Windows.Threading.DispatcherTimer основаны не на планировщике потоков Windows, а на сообщении WM_TIMER. Из-за того, как это сообщение работает — он генерируется "на лету", когда петля сообщения потока проверяет очередь сообщений в зависимости от того, прошло ли время истечения для таймера & hellip; в некотором роде оно похоже на обход опроса, описанный в другой ответ на ваш вопрос — он невосприимчив к задержкам, которые в противном случае были бы вызваны приостановкой компьютера.

Вы заявили, что ваш сценарий включает в себя программу WPF, поэтому вы можете обнаружить, что лучшим решением является использование класса DispatcherTimer вместо System.Timers.Timer.

Если вы решите, что вам нужна реализация таймера, которая не привязана к потоку пользовательского интерфейса, здесь версия System.Threading.Timer, которая будет правильно учитывать временные затраты во время приостановления:

class SleepAwareTimer : IDisposable
{
    private readonly Timer _timer;
    private TimeSpan _dueTime;
    private TimeSpan _period;
    private DateTime _nextTick;
    private bool _resuming;

    public SleepAwareTimer(TimerCallback callback, object state, TimeSpan dueTime, TimeSpan period)
    {
        _dueTime = dueTime;
        _period = period;
        _nextTick = DateTime.UtcNow + dueTime;
        SystemEvents.PowerModeChanged += _OnPowerModeChanged;

        _timer = new System.Threading.Timer(o =>
        {
            _nextTick = DateTime.UtcNow + _period;
            if (_resuming)
            {
                _timer.Change(_period, _period);
                _resuming = false;
            }
            callback(o);
        }, state, dueTime, period);
    }

    private void _OnPowerModeChanged(object sender, PowerModeChangedEventArgs e)
    {
        if (e.Mode == PowerModes.Resume)
        {
            TimeSpan dueTime = _nextTick - DateTime.UtcNow;

            if (dueTime < TimeSpan.Zero)
            {
                dueTime = TimeSpan.Zero;
            }

            _timer.Change(dueTime, _period);
            _resuming = true;
        }
    }

    public void Change(TimeSpan dueTime, TimeSpan period)
    {
        _dueTime = dueTime;
        _period = period;
        _nextTick = DateTime.UtcNow + _dueTime;
        _resuming = false;
        _timer.Change(dueTime, period);
    }

    public void Dispose()
    {
        SystemEvents.PowerModeChanged -= _OnPowerModeChanged;
        _timer.Dispose();
    }
}

Открытый интерфейс для System.Threading.Timer и интерфейс подмножества выше, скопированный из этого класса, отличается от того, что вы найдете на System.Timers.Timer, но он выполняет то же самое. Если вам действительно нужен класс, который работает точно так же, как System.Timers.Timer, не сложно будет адаптировать вышеупомянутый метод в соответствии с вашими потребностями.

Ответ 3

System.Timers.Timer - это серверный таймер (прошедшее событие использует Threadpool. Точность, чем другие таймеры). Когда ваш компьютер переходит в спящий режим или в спящий режим, все состояние вашей программы сохраняется в ОЗУ. То же самое касается состояния вашего приложения. После того, как ваша система встанет, ваше состояние приложения будет восстановлено (вместе с таймером) операционной системой. Не будет хорошей идеей "что-то сделать" или попытаться обнаружить эти события. Это возможно из службы Windows. Оставьте его OS, чтобы выполнить свою работу.