Warning: file_get_contents(/data/phpspider/zhask/data//catemap/2/.net/21.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
C# System.Timers.Timer在WPF应用程序中休眠和休眠后的行为如何?_C#_.net_Wpf_Timer - Fatal编程技术网

C# System.Timers.Timer在WPF应用程序中休眠和休眠后的行为如何?

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

我正在WPF应用程序中使用System.Timers.Timer。我想了解在计算机休眠和休眠后,计时器是如何工作的。在计算机从hibernate恢复后,我的应用程序出现了一些奇怪的问题

我应该如何处理计时器,以及当计算机处于休眠/睡眠模式时,计时器的行为如何?

我有一个午夜计时器,它应该在每个午夜工作,以重置UI上的默认值

以下是创建计时器的代码:

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运行的代码

睡眠与Hibernate澄清:睡眠与Hibernate的主要区别在于,在睡眠中,所有内容都保存在RAM中,而Hibernate将当前状态保存到磁盘。hibernate的主要优点是RAM不再需要电源,因此消耗的能量更少。这就是为什么在处理使用有限能量的笔记本电脑或其他设备时,建议使用hibernate而不是sleep

这就是说,在这两种情况下,程序的执行没有区别,因为它们都被暂停。不幸的是,System.Timers.Timer无法“唤醒”计算机,因此您无法强制执行在凌晨12:01左右运行的代码

我相信还有其他方法可以“唤醒”计算机,但除非你这样做,否则你能做的最好的方法就是在计时器的下一个“轮询事件”中运行你的“事件”,在它退出睡眠/休眠状态之后

这是因为在休眠时,它只是将事件处理延迟了与休眠时相同的时间吗

当计算机处于挂起模式(即睡眠或休眠)时,它不会执行任何操作。这尤其包括处理唤醒监视计时器事件队列的线程的调度程序没有运行,因此线程在恢复执行以处理下一个计时器方面没有取得任何进展

没那么多
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);
        }
    }    
}