visual timers tick new interval form evento timer

timers - timer elapsed c#



Haz un.net System.Timers.Timer dispara inmediatamente (3)

Estoy usando un temporizador para ejecutar un evento periódicamente en un intervalo razonablemente largo (2 minutos). Esto está funcionando bien. Sin embargo, me gustaría que el evento se dispare inmediatamente cuando se crea el temporizador (en lugar de esperar 2 minutos).

Tenga en cuenta que no puedo hacer esto solo llamando al método, ya que toma algún tiempo ejecutarlo y bloquearía la aplicación. Necesito que el temporizador se dispare normalmente y ejecute el evento en un hilo separado.

La mejor manera de pensar en hacer esto en este momento es subclasificar el temporizador y crear un método TriggerManually que haría algo como esto:

  • Desactivar el reinicio automático
  • Establecer el intervalo a 1 ms.
  • Habilitar el temporizador

Esto activaría el evento transcurrido de inmediato, y podría volver a poner todos los ajustes a la normalidad.

Aunque parece un poco rotunda. Hay una mejor manera de hacerlo?


¿No podría simplemente llamar a su controlador de eventos para el evento transcurrido manualmente?

Incluso si esperaba que se ejecutara en un subproceso de agrupación de hebras, podría invocarlo.

class Blah { private Timer mTimer; public Blah() { mTimer = new Timer(120000); ElapsedEventHandler handler = new ElapsedEventHandler(Timer_Elapsed); mTimer.Elapsed += handler; mTimer.Enabled = true; //Manually execute the event handler on a threadpool thread. handler.BeginInvoke(this, null, new AsyncCallback(Timer_ElapsedCallback), handler); } private static void Timer_Elapsed(object source, ElapsedEventArgs e) { //Do stuff... } private void Timer_ElapsedCallback(IAsyncResult result) { ElapsedEventHandler handler = result.AsyncState as ElapsedEventHandler; if (handler != null) { handler.EndInvoke(result); } } }



Me gustó la respuesta de Rob Cooke, así que construí una pequeña clase EagerTimer que subclasifica System.Timers.Timer y agrega esta funcionalidad. (Con pistas de these articles ).

Sé que podría usar System.Threading.Timer lugar, pero esto es simple y funciona bien en mi aplicación.

EagerTimer

/// <summary> // EagerTimer is a simple wrapper around System.Timers.Timer that // provides "set up and immediately execute" functionality by adding a // new AutoStart property, and also provides the ability to manually // raise the Elapsed event with RaiseElapsed. /// </summary> public class EagerTimer : Timer { public EagerTimer() : base() { } public EagerTimer(double interval) : base(interval) { } // Need to hide this so we can use Elapsed.Invoke below // (otherwise the compiler complains) private event ElapsedEventHandler _elapsedHandler; public new event ElapsedEventHandler Elapsed { add { _elapsedHandler += value; base.Elapsed += value; } remove { _elapsedHandler -= value; base.Elapsed -= value; } } public new void Start() { // If AutoStart is enabled, we need to invoke the timer event manually if (AutoStart) { this._elapsedHandler.BeginInvoke(this, null, new AsyncCallback(AutoStartCallback), _elapsedHandler); // fire immediately } // Proceed as normal base.Start(); } private void AutoStartCallback(IAsyncResult result) { ElapsedEventHandler handler = result.AsyncState as ElapsedEventHandler; if (handler != null) handler.EndInvoke(result); } // Summary: // Gets or sets a value indicating whether the EagerTimer should raise // the System.Timers.Timer.Elapsed event immediately when Start() is called, // or only after the first time it elapses. If AutoStart is false, EagerTimer behaves // identically to System.Timers.Timer. // // Returns: // true if the EagerTimer should raise the System.Timers.Timer.Elapsed // event immediately when Start() is called; false if it should raise the System.Timers.Timer.Elapsed // event only after the first time the interval elapses. The default is true. [Category("Behavior")] [DefaultValue(true)] [TimersDescription("TimerAutoStart")] public bool AutoStart { get; set; } /// <summary> /// Manually raises the Elapsed event of the System.Timers.Timer. /// </summary> public void RaiseElapsed() { if (_elapsedHandler != null) _elapsedHandler(this, null); } }

Pruebas unitarias

[TestClass] public class Objects_EagerTimer_Tests { private const int TimerInterval = 10; // ms private List<DateTime> _timerFires = new List<DateTime>(); private DateTime _testStart; [TestInitialize] public void TestSetup() { _timerFires.Clear(); _testStart = DateTime.Now; } [TestMethod] public void Objects_EagerTimer_WithAutoStartDisabled() { // EagerTimer should behave as a normal System.Timers.Timer object var timer = new EagerTimer(TimerInterval); timer.AutoReset = false; timer.Elapsed += timerElapsed; timer.Start(); // Wait (not enough time for first interval) Thread.Sleep(5); Assert.IsFalse(_timerFires.Any()); // Wait a little longer Thread.Sleep(TimerInterval); Assert.AreEqual(1, _timerFires.Count); } [TestMethod] public void Objects_EagerTimer_WithAutoStartEnabled() { // EagerTimer should fire immediately on Start() var timer = new EagerTimer(TimerInterval); timer.AutoReset = false; timer.AutoStart = true; timer.Elapsed += timerElapsed; timer.Start(); // Wait (not enough time for first interval) Thread.Sleep(5); Assert.IsTrue(_timerFires.Any()); // Wait a little longer, now it will have fired twice Thread.Sleep(TimerInterval); Assert.AreEqual(2, _timerFires.Count); } [TestMethod] public void Objects_EagerTimer_WhenRaisingManually() { // EagerTimer should fire immediately on Start() var timer = new EagerTimer(TimerInterval); timer.AutoReset = false; timer.AutoStart = false; timer.Elapsed += timerElapsed; Assert.IsFalse(_timerFires.Any()); timer.RaiseElapsed(); Assert.IsTrue(_timerFires.Any()); } private void timerElapsed(object sender, ElapsedEventArgs e) { _timerFires.Add(DateTime.Now); } }