Saltar al contenido

¿Cómo se comporta System.Timers.Timer en la aplicación WPF, después de Hibernate y Sleep?

Ten en cuenta que en las ciencias informáticas cualquier problema casi siempre tiene más de una soluciones, así que nosotros aquí te mostraremos la mejor y más eficiente.

Solución:

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), probablemente verá algún comportamiento “extraño”. Dado que no especifica cuál es ese comportamiento ‘extraño’, voy a asumir que el temporizador de su programa suena más tarde de lo que debería.

Explicación: El problema de ir a dormir / hibernar es que todos los programas están suspendidos. Esto significa que sus temporizadores no se actualizan y, por lo tanto, cuando duerme / hiberna y vuelve, es como si estuviera congelado durante ese período de tiempo en el que estaba durmiendo / hibernando. Esto significa que si tiene un temporizador configurado para que se apague en una hora y su computadora entra en suspensión en la marca de 15 minutos, una vez que se despierta, tendrá otros 45 minutos, independientemente de cuánto tiempo estuvo durmiendo la computadora.

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

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

Actualizar: La solución presentada anteriormente funcionará y completaré un par de detalles para ayudarlo a implementarla.

  • En lugar de crear o eliminar nuevos temporizadores, cree UNO temporizador para usar eso es PERIÓDICO (la propiedad AutoReset se establece en true)

  • El intervalo del temporizador único debe NO configurarse 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 verifica si el ‘evento’ debe ejecutarse). La elección debe ser un equilibrio entre eficiencia y precisión. Si NECESITA que se ejecute REALMENTE cerca de las 12:01 a.m., establezca el intervalo en alrededor de 5-10 segundos. Si es menos importante que sea exactamente a las 12:01 a. M., Puede aumentar el intervalo a algo así como de 1 a 10 minutos.

  • Debe mantener una fecha y hora de cuándo ocurrió la última ejecución O cuándo debería ocurrir la próxima ejecución. Preferiría ‘cuándo debería ocurrir la próxima ejecución’ para que no esté haciendo (LastExecutionTime + EventInterval) cada vez que transcurre el temporizador, solo estará comparando la hora actual y la hora en que debería ocurrir el evento.

  • Una vez que transcurre el temporizador y el evento DEBERÍAN ocurra (en algún lugar alrededor de las 12:01 AM), debe actualizar el DateTime almacenado y luego ejecutar el código que desea ejecutar a las 12:01 AM.

Aclaración de sueño frente a hibernación: La principal diferencia entre suspensión e hibernación es que, en suspensión, todo se mantiene en la RAM, mientras que la 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, consume menos energía. Es por eso que se recomienda usar la hibernación en lugar de la suspensión 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 ‘despierta’ una computadora y, por lo tanto, no puede hacer que su código se ejecute a las ~ 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 modo de suspensión / hibernación.

¿Se debe a que mientras hiberna simplemente pospone el manejo de eventos por la misma cantidad de tiempo que estuvo hibernado?

Mientras la computadora está en modo suspendido (es decir, suspensión o hibernación), no hace nada. Esto incluye, en particular, el programador que maneja la activación del subproceso que está monitoreando la cola de eventos del temporizador que no se está ejecutando y, por lo tanto, el subproceso no avanza 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 WM_TIMER mensaje. Debido a la forma en que funciona este mensaje: se genera “sobre la marcha” cuando el bucle de mensajes de un hilo verifica la cola de mensajes, en función de si el tiempo de vencimiento del temporizador ha pasado … en cierto modo, es similar a la solución alternativa de sondeo. descrito en la otra respuesta a su pregunta: es inmune a los retrasos que de otro modo serían causados ​​por la suspensión de la computadora.

Ha indicado que su escenario involucra un programa WPF, por lo que puede encontrar que su mejor solución es usar el DispatcherTimer clase, en lugar de System.Timers.Timer.

Si decides que necesitas una implementación de temporizador que no esté vinculada al hilo de la interfaz de usuario, aquí tienes una versión de System.Threading.Timer que tendrá en cuenta correctamente el tiempo invertido 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 quieres una clase que funcione exactamente como System.Timers.Timer, no debería ser difícil adaptar la técnica anterior para satisfacer sus necesidades.

Recuerda algo, que puedes optar por la opción de agregar una reseña .

¡Haz clic para puntuar esta entrada!
(Votos: 0 Promedio: 0)



Utiliza Nuestro Buscador

Deja una respuesta

Tu dirección de correo electrónico no será publicada. Los campos obligatorios están marcados con *