【问题标题】:Make a .net System.Timers.Timer fire immediately立即启动 .net System.Timers.Timer
【发布时间】:2010-08-02 03:07:06
【问题描述】:

我正在使用计时器以相当长的时间间隔(2 分钟)定期运行事件。这工作正常。但是,我希望在创建计时器时立即触发事件(而不是等待 2 分钟)。

请注意,我不能仅通过调用该方法来执行此操作,因为它需要一些时间来运行并且会阻塞应用程序。我需要计时器正常触发并在单独的线程中运行事件。

目前我能想到的最好方法是将计时器子类化并创建一个TriggerManually 方法来执行以下操作:

  • 关闭自动重置
  • 将间隔设置为 1 毫秒
  • 启用计时器

这将立即触发 elapsed 事件,我可以将所有设置恢复正​​常。

虽然看起来有点迂回。有没有更好的方法?

【问题讨论】:

    标签: timer


    【解决方案1】:

    你不能手动调用你的事件处理程序来处理经过的事件吗?

    即使您希望它在线程池线程上执行,您也可以调用它。

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

    【讨论】:

      【解决方案2】:

      我喜欢 Rob Cooke 的回答,所以我构建了一个小的 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);
          }
      }
      

      【讨论】:

      • 如果你把它变成我可以用 Nuget 拉下来的东西会很好。
      • 顺便说一下,我在你的类中添加了一个方便的方法来自动启动作为Start调用的一部分;我建议您在答案中添加它:public void Start(bool withAutoStart) { if (withAutoStart) { AutoStart = true; } Start(); }
      【解决方案3】:

      你能改用 System.Threading.Timer 吗? 它有一个构造函数,可以让您选择间隔和延迟(可以设置为 0 以立即开始)。 http://msdn.microsoft.com/en-us/library/2x96zfy7.aspx

      【讨论】:

      • @Mitch - 只是证明你是人类,尽管你的代表很疯狂 :)
      • 是的,这行得通。 System.Threading.Timer 使用起来有点困难,但它可以满足我的需要。谢谢!
      • 警告System.Threading.Timer 的回调will crash your app 中存在未处理的异常。
      猜你喜欢
      • 2016-09-19
      • 2021-01-30
      • 2010-11-14
      • 1970-01-01
      • 2016-06-05
      • 1970-01-01
      • 2011-08-10
      • 2012-05-13
      • 1970-01-01
      相关资源
      最近更新 更多