2011-11-22 10 views
19

Estoy usando System.Timers.Timer en mi aplicación WPF. Quiero entender cómo se comporta Timer, después de que la computadora está hibernada, y dormir. Tengo algunos problemas extraños con mi aplicación, después de que la computadora se reanude de hibernar.¿Cómo se comporta System.Timers.Timer en la aplicación WPF, después de Hibernate y Sleep?

¿Cómo debo manejar los temporizadores y cómo se comportan cuando la computadora está en modo de hibernación/suspensión?

Tengo un temporizador de medianoche que debería funcionar cada medianoche para restablecer los valores predeterminados en la IU.

Aquí está el código que crea el temporizador:

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(); 
     } 

En contructor de la página de interfaz de usuario, que llamo el método que llama ResestMidnightTimer() y crea el temporizador de facto. Después de eso, el cronómetro solo espera la noche.

Cuando llega la noche (en realidad es la 12:01 AM), el temporizador funciona, restablece los valores predeterminados como se esperaba y luego descarta el temporizador existente. Finalmente, crea un nuevo temporizador de medianoche para el día siguiente. Pero si trato de hibernar la computadora durante ese día, el temporizador de medianoche no funcionará y no restablecerá los valores predeterminados.

¿Es porque mientras hiberna, simplemente pospone el manejo del evento por la misma cantidad de tiempo que estaba hibernado?

+6

¿Cuáles son los problemas que está viendo (solo curiosidad)? – M4N

+0

@ M4N He editado la pregunta. – User1234

Respuesta

14

Esto depende de cómo esté usando sus temporizadores. Si los está utilizando para iniciar algún evento que ocurre con poca frecuencia (más de un par de minutos), entonces probablemente verá algún comportamiento "extraño". Como no especifica qué es ese comportamiento "extraño", supongo que el temporizador de su programa se apaga más tarde de lo debido.

Explicación: El problema con el modo de suspensión/hibernación es que todos los programas están suspendidos. Esto significa que sus temporizadores no se están actualizando y, por lo tanto, cuando usted duerme/hiberna y regresa, es como si estuviera congelado durante ese período de tiempo que estaba durmiendo/hibernando. Esto significa que si tiene un temporizador configurado para que se apague en una hora y su computadora se apaga en la marca de los 15 minutos, una vez que se despierte tendrá otros 45 minutos, independientemente de cuánto durmió la computadora.

Solución: Una solución sería mantener un DateTime alrededor de la última vez que ocurrió el evento. Luego, haga que un temporizador se apague periódicamente (cada 10 segundos o 10 minutos, dependiendo de la precisión deseada) y verifique el Fecha y hora de la última ejecución. Si la diferencia entre ahora y el último tiempo de ejecución es mayor o igual que el intervalo deseado, ENTONCES ejecuta la ejecución.

Esto lo arreglará de modo que si un evento 'debería haber' ocurrido durante la suspensión/hibernación, comenzará en el momento en que regrese de dormir/hibernar.

Actualización: La solución presentada arriba funcionará y voy a completar un par de detalles para ayudarlo a implementarla.

  • En lugar de crear/eliminación de los nuevos contadores de tiempo, crear UNO temporizador para utilizar lo que se recurrente (la propiedad AutoReset se establece en true)

  • El intervalo del temporizador único debe NO se configura de acuerdo con la próxima vez que ocurra el evento. En su lugar, debe establecerse en un valor que elija que represente la frecuencia de sondeo (con qué frecuencia se verifica si se debe ejecutar el 'evento'). La elección debe ser un equilibrio de eficiencia y precisión. Si NECESITA que se ejecute REALMENTE cerca de las 12:01 a.m., establezca el intervalo entre 5 y 10 segundos. Si es menos importante que sea exactamente a las 12:01 a.m., puede aumentar el intervalo a entre 1 y 10 minutos.

  • Debe mantener alrededor de un DateTime de cuando se produjo la última ejecución O cuando debería producirse la próxima ejecución. Preferiría "cuando la próxima ejecución ocurra" para que no esté haciendo (LastExecutionTime + EventInterval) cada vez que transcurra el tiempo del temporizador, simplemente estará comparando la hora actual y la hora en que debería ocurrir el evento.

  • Una vez que el tiempo termine y el evento DEBERÍAN producirse (en algún lugar alrededor 12:01a.m.), debe actualizar el DateTime almacenada y luego ejecutar el código que desea ejecutar a las 12:01 PM.

sueño vs. Hibernate Aclaración: La principal diferencia entre el sueño y la hibernación es que en el sueño, todo se mantiene en la memoria RAM, mientras que hibernación guarda el estado actual en el disco. La principal ventaja de la hibernación es que la RAM ya no necesita energía y por lo tanto gasta menos energía. Esta es la razón por la que se recomienda utilizar hibernación durante el sueño cuando se trata de computadoras portátiles u otros dispositivos que usan una cantidad finita de energía.

Dicho esto, no hay diferencia en la ejecución de los programas, ya que se suspenden en ambos casos. Desafortunadamente, System.Timers.Timer no 'activa' una computadora y por lo tanto no puede hacer que su código se ejecute a ~ 12: 01 AM.

Creo que hay OTRAS formas de "despertar" una computadora, pero a menos que vaya por esa ruta lo mejor que puede hacer es ejecutar su 'evento' durante el próximo 'evento de sondeo' de su temporizador después de que salga del sueño /hibernar.

+0

+1 para el examen de la sugerencia de estado – Unsliced

+0

+1 para comprobar la diferencia horaria. – PRR

+0

@docmanhattan ¡Muchas gracias! He editado la pregunta y le he agregado más detalles. ¿Qué crees que sería la solución de trabajo en este caso? – User1234

1

System.Timers.Timer es un temporizador basado en servidor (el evento transcurrido utiliza Threadpool. Más preciso que otros temporizadores). Cuando su computadora entra en modo de suspensión o en modo de hibernación, todo el estado de su programa se almacena en la memoria RAM. Lo mismo aplica para su estado de aplicación. Una vez que el sistema esté listo, el sistema operativo restaurará (junto con el temporizador) el estado de la aplicación. No será una buena idea "hacer algo" o tratar de detectar estos eventos. Es posible desde un servicio de Windows sin embargo. Deje que el sistema operativo haga su trabajo.

3

¿Es porque mientras hiberna solo pospone el manejo del evento por la misma cantidad de tiempo que estaba hibernado?

Mientras la computadora está en modo suspendido (es decir, en suspensión o hibernación), no hace nada. Esto incluye, en particular, que el planificador que maneja el despertar del subproceso que está supervisando la cola de eventos del temporizador no se está ejecutando y para que el subproceso no esté haciendo ningún progreso hacia la reanudación de la ejecución para manejar el siguiente temporizador.

No es tanto que el evento sea explícitamente pospuesto per se. Pero sí, ese es el efecto neto.

En algunos casos, es posible utilizar una clase de temporizador que no tenga este problema. Ambos System.Windows.Forms.Timer y System.Windows.Threading.DispatcherTimer se basan no en el programador de subprocesos de Windows, sino en el mensaje WM_TIMER. Debido a la forma en que funciona este mensaje —, se genera "sobre la marcha" cuando el ciclo de mensajes de un hilo comprueba la cola de mensajes, en función de si ha pasado el tiempo de caducidad del temporizador. De alguna manera, es similar a la interrogación descrito en the other answer to your question — es inmune a las demoras que de otro modo serían causadas por la suspensión de la computadora.

Ha afirmado que su situación implica un programa WPF, por lo que es posible que su mejor solución sea utilizar la clase DispatcherTimer, en lugar de System.Timers.Timer.

Si decide que necesita una aplicación de temporizador que no está vinculada al hilo de interfaz de usuario, he aquí una versión de System.Threading.Timer que tener en cuenta correctamente pasar tiempo mientras está suspendido:

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(); 
    } 
} 

La interfaz pública para System.Threading.Timer, y la interfaz de subconjunto anterior copiada de esa clase, es diferente de lo que encontrará en System.Timers.Timer, pero logra lo mismo. Si realmente desea una clase que funcione exactamente como System.Timers.Timer, no debería ser difícil adaptar la técnica anterior para satisfacer sus necesidades.

Cuestiones relacionadas