2010-08-02 53 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); 
    } 
}