2010-08-02 10 views
15

Używam programatora do okresowego uruchamiania zdarzenia w odpowiednio długim interwale (2 minuty). To działa dobrze. Jednak chciałbym, aby zdarzenie zostało natychmiast uruchomione po utworzeniu stopera (zamiast czekać 2 minuty).Natychmiastowe uruchomienie ognia systemu .net System.Timers.Timer

Pamiętaj, że nie mogę tego zrobić, po prostu wywołując metodę, ponieważ uruchomienie i zablokowanie aplikacji zajmie trochę czasu. Potrzebuję czasomierza do wystrzelenia jak zwykle i uruchomienia zdarzenia w osobnym wątku.

Najlepszym sposobem mogę myśleć robi to w tej chwili jest instacji programator i utworzenie TriggerManually metodę, która zrobi coś takiego:

  • Włącz automatyczne resetowanie się
  • Ustaw interwał do 1ms
  • Włącz timer

To wywoła zdarzenie, jaki upłynął od razu, a może umieścić wszystkie ustawienia z powrotem do normalności.

Wydaje się jednak trochę rondo. Czy istnieje lepszy sposób na zrobienie tego?

Odpowiedz

4

Czy można zamiast tego użyć System.Threading.Timer? Ma konstruktora, który pozwala wybrać interwał, a także opóźnienie (które można ustawić na 0, aby rozpocząć od razu). http://msdn.microsoft.com/en-us/library/2x96zfy7.aspx

+0

@Mitch - tylko dowodzi, że jesteś człowiekiem pomimo swojego szalonego rep :) – Gishu

+0

Tak, to działa. System.Threading.Timer jest nieco trudniejszy w użyciu, ale robi to, czego potrzebuję. Dzięki! – Nathan

+2

Należy pamiętać, że nieobsługiwany wyjątek w wywołaniu zwrotnym 'System.Threading.Timer' [spowoduje awarię aplikacji] (http://stackoverflow.com/questions/1718598/throwing-exceptions-in-callback-method-for-timers). –

12

Nie możesz po prostu ręcznie wywołać obsługi zdarzeń dla zdarzenia, które upłynęło?

Nawet jeśli spodziewałeś się, że zostanie wykonany w wątku puli wątków, możesz go wywołać.

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

lubiłem odpowiedź Rob Cooke'a, więc zbudował mały EagerTimer klasę, która podklasy System.Timers.Timer i dodaje tę funkcjonalność. (Z podpowiedziami od these)

Wiem, że mógłbym zamiast tego użyć System.Threading.Timer, ale jest to proste i działa dobrze w mojej aplikacji.

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

Jednostka Testy

[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); 
    } 
} 
Powiązane problemy