2010-08-02 100 views
15

我正在使用定時器以相當長的時間間隔(2分鐘)定期運行一個事件。這工作正常。不過,我希望事件在創建計時器時立即觸發(而不是等待2分鐘)。使一個.net System.Timers.Timer立即觸發

請注意,我不能通過調用方法來做到這一點,因爲它需要一些時間來運行,並會阻止應用程序。我需要定時器正常觸發,並在單獨的線程中運行事件。

我能想到此刻這樣做是繼承了計時器,創造一個TriggerManually方法,會做這樣的事情的最好方法:

  • 打開自動復位關閉
  • 時間間隔設置爲1ms的
  • 啓用定時器

這將觸發事件經過的直線距離,我可以把所有的設置恢復正​​常。

雖然似乎有點迂迴。有沒有更好的方法來做到這一點?

回答

4

您可以使用System.Threading.Timer嗎? 它有一個構造函數,可以讓您選擇間隔以及延遲(可以將其設置爲0以立即開始)。 http://msdn.microsoft.com/en-us/library/2x96zfy7.aspx

+0

@米奇 - 只是證明你是你瘋了代表:) – Gishu 2010-08-02 05:49:22

+0

沒錯的人inspite,這個工程。 System.Threading.Timer有點難以使用,但它做我需要的。謝謝! – Nathan 2010-08-02 13:26:54

+2

被警告,System.Threading.Timer的回調中有一個未處理的異常[會導致你的應用崩潰](http://stackoverflow.com/questions/1718598/throwing-exceptions-in-callback-method-for-timers )。 – 2014-07-30 15:00:46

12

難道你不能只是手動調用你的事件處理程序爲已逝的事件?

即使您期待它在線程池線程上執行,也可以調用它。

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

我喜歡羅布·庫克的回答,所以我建立了一個小EagerTimer類的子類System.Timers.Timer並添加此功能。 (提示從thesearticles

我知道我可以使用System.Threading.Timer來代替,但這很簡單,在我的應用程序中效果很好。

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

單元測試

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