C# System.Timers.Timer在WPF应用程序中休眠和休眠后的行为如何?
我正在WPF应用程序中使用System.Timers.Timer。我想了解在计算机休眠和休眠后,计时器是如何工作的。在计算机从hibernate恢复后,我的应用程序出现了一些奇怪的问题 我应该如何处理计时器,以及当计算机处于休眠/睡眠模式时,计时器的行为如何? 我有一个午夜计时器,它应该在每个午夜工作,以重置UI上的默认值 以下是创建计时器的代码:C# System.Timers.Timer在WPF应用程序中休眠和休眠后的行为如何?,c#,.net,wpf,timer,C#,.net,Wpf,Timer,我正在WPF应用程序中使用System.Timers.Timer。我想了解在计算机休眠和休眠后,计时器是如何工作的。在计算机从hibernate恢复后,我的应用程序出现了一些奇怪的问题 我应该如何处理计时器,以及当计算机处于休眠/睡眠模式时,计时器的行为如何? 我有一个午夜计时器,它应该在每个午夜工作,以重置UI上的默认值 以下是创建计时器的代码: private void ResetMidnightTimer() { // kill the old
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();
}
在UI页面的构造函数中,我调用了调用ResestMidnightTimer()并实际创建计时器的方法。在那之后,计时器只是等待夜晚
当夜间(实际上是12:01 AM)到来时,计时器工作,按预期重置默认值,然后处理现有计时器。最后,它为第二天创建了一个新的午夜计时器。
但是如果我尝试在那天休眠计算机,午夜计时器将不工作,也不会重置默认值
这是因为在休眠时,它只是将事件处理延迟了与休眠时相同的时间吗?System.Timers.Timer是基于服务器的计时器(已用事件使用线程池。比其他计时器更准确)。当您的计算机进入睡眠模式或休眠模式时,程序的所有状态都存储在RAM中。应用程序状态也是如此。一旦系统启动,操作系统将恢复应用程序状态(以及计时器)。“做点什么”或尝试检测这些事件不是一个好主意。不过,这可以通过Windows服务实现。让操作系统来完成它的工作。这取决于您如何使用计时器。如果您使用它们来启动一些不经常发生的事件(超过几分钟),那么您可能会看到一些“奇怪”的行为。由于您没有指定“奇怪”的行为是什么,我将假设您的程序的计时器比它应该的时间晚 说明:进入睡眠/休眠状态的问题是所有程序都被挂起。这意味着你的计时器没有被更新,因此当你睡觉/冬眠并回来时,就好像你在睡觉/冬眠的那段时间里被冻结了一样。这意味着,如果您将计时器设置为在一小时内关闭,并且您的计算机在15分钟后进入睡眠状态,那么一旦它醒来,无论计算机睡眠多长时间,它都还有45分钟的时间 解决方案:一个修复方法是将日期时间保留在上次事件发生的时间附近。然后,让计时器定期关闭(每10秒或10分钟,取决于所需的精度),并检查最后一次执行的日期时间。如果现在和上次执行时间之间的差异大于或等于所需的间隔,则运行执行 这将修复它,以便如果在睡眠/休眠期间“应该”发生事件,它将在您从睡眠/休眠返回时启动 更新:上面介绍的解决方案将起作用,我将填写一些详细信息以帮助您实现它
- 与其创建/处理新计时器,不如创建一个定期使用的计时器(AutoReset属性设置为true)
- 单个计时器的间隔应而不是根据事件下次发生的时间进行设置。相反,它应该设置为您选择的一个值,该值将表示轮询频率(它检查“事件”是否应该运行的频率)。选择应该是效率和精度的平衡。如果您需要它在凌晨12:01左右运行,那么您可以将时间间隔设置为5-10秒左右。如果不太重要的是正好在凌晨12:01,您可以将间隔时间增加到1-10分钟左右
- 您需要保留上一次执行的日期时间或下一次执行的日期时间。我更喜欢“下一次执行应该在什么时候发生”,这样你就不会在每次计时器过期时都执行(LastExecutionTime+EventInterval),你只需要比较当前时间和事件应该发生的时间
- 一旦计时器过期且事件应该发生(大约12:01 AM),您应该更新存储的日期时间,然后运行希望在12:01 AM运行的代码
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();
}
}
using Microsoft.Win32;
using System;
using System.Threading;
class AlarmSleepTolerant : IDisposable
{
readonly Timer _timer;
DateTime? _alarmUtcOpt;
/// <summary>
/// Constructor
/// </summary>
/// <param name="callback"></param>
/// <param name="alarmUtcOpt"></param>
/// <param name="stateOpt"></param>
public AlarmSleepTolerant(TimerCallback callback, DateTime? alarmUtcOpt = null, object stateOpt = null)
{
SystemEvents.PowerModeChanged += _OnPowerModeChanged;
_timer = new Timer(callback, stateOpt, Timeout.Infinite, Timeout.Infinite);
SetAlarmTime(alarmUtcOpt);
}
/// <summary>
/// Set the current alarm, if alarmUtc is <= UtcNow, then the alarm goes off immediately.
/// Pass null to disable the alarm.
/// </summary>
/// <param name="alarmUtc"></param>
public void SetAlarmTime(DateTime? alarmUtcOpt = null)
{
lock (_timer)
{
_alarmUtcOpt = alarmUtcOpt;
if (!alarmUtcOpt.HasValue)
{
_timer.Change(Timeout.Infinite, Timeout.Infinite); // disables the timer
}
else
{
TimeSpan dueIn = _alarmUtcOpt.Value - DateTime.UtcNow;
_timer.Change(dueIn.Ticks <= 0 ? 0 : (long)dueIn.TotalMilliseconds, Timeout.Infinite);
}
}
}
public void Dispose()
{
SystemEvents.PowerModeChanged -= _OnPowerModeChanged;
_timer.Dispose();
}
void _OnPowerModeChanged(object sender, PowerModeChangedEventArgs e)
{
// Timers are based on intervals rather than absolute times so they
// need to be adjusted upon a resume from sleep.
//
// If the alarm callback was missed during sleep, it will be called now.
//
if (e.Mode == PowerModes.Resume)
{
lock (_timer)
SetAlarmTime(_alarmUtcOpt);
}
}
}