2010-08-02 13 views
15

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).Haga que un .Net System.Timers.Timer encienda inmediatamente

Tenga en cuenta que no puedo hacer esto simplemente llamando al método, ya que lleva tiempo ejecutarlo y bloquearía la aplicación. Necesito el temporizador para disparar normalmente y ejecutar el evento en un hilo separado.

La mejor manera que puedo pensar en hacer esto en el momento en que se herede de temporizador y la creación de un método TriggerManually que hacer algo como esto:

  • Encienda reinicio automático fuera
  • establecer el intervalo de 1 ms
  • activar el temporizador

Esto activará el evento transcurrido enseguida, y pude poner todos los ajustes vuelvan a la normalidad.

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

Respuesta

4

¿Podría usar un System.Threading.Timer en su lugar? Tiene un constructor que le permite elegir el intervalo así como la demora (que se puede establecer en 0 para comenzar inmediatamente). http://msdn.microsoft.com/en-us/library/2x96zfy7.aspx

+0

@Mitch - sólo demuestra que eres humano a pesar de su representante loco :) – Gishu

+0

Sí, esto funciona. System.Threading.Timer es un poco más difícil de usar, pero hace lo que necesito. ¡Gracias! – Nathan

+2

Tenga en cuenta que una excepción no controlada en la devolución de llamada de 'System.Threading.Timer' [bloqueará su aplicación] (http://stackoverflow.com/questions/1718598/throwing-exceptions-in-callback-method-for-timers) –

12

¿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 grupo de subprocesos, 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); 
     } 
    } 
} 
5

me gustó la respuesta de Rob Cooke, por lo que construyeron una pequeña clase que EagerTimer subclases System.Timers.Timer y añade esta funcionalidad. (Con sugerencias de thesearticles)

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

EagerTimer Unidad

/// <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 de

[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); 
    } 
} 
Cuestiones relacionadas