停止System.Timers.Timer在设置AutoReset后开始触发Elapsed事件

时间:2018-03-05 16:55:57

标签: c# .net timer

我检测到System.Timers.Timer的奇怪行为:

  1. 创建计时器的间隔为1秒,AutoReset设置为false
  2. 启动计时器,并在1秒内触发Elapsed事件。由于timer.Enabled设置为false,因此内部事件处理程序AutoResetfalse
  3. 然后,由于计时器停止,因此不会触发Elapsed个事件。
  4. 现在我们将timer.AutoReset设置为trueElapsed事件每1秒开始触发一次。 timer.Enabled仍然是假的。
  5. 这是Timer代码中的错误还是可能有这种奇怪行为的原因?

    根据我的理解,即使我们更改了AutoReset属性,停止的计时器也不应该开始触发事件。 我知道在计时器停止后可能会触发Elapsed事件,因为执行已在线程池中排队(问题在此question中描述)。但是它没有解释为什么停止计时器开始触发事件。

    以下是重现问题的最小样本:

    class Program
    {
        private static System.Timers.Timer timer;
    
        static void Main(string[] args)
        {
            timer = new System.Timers.Timer
            {
                AutoReset = false,
                Interval = 1000
            };
            timer.Elapsed += TimerTick;
    
            LogMessage("Starting timer...");
            timer.Start();
            Thread.Sleep(3000);
    
            LogMessage($"Timer is {(timer.Enabled ? "enabled" : "stopped")}");
            LogMessage("Setting AutoReset to true");
            timer.AutoReset = true;
    
            Thread.Sleep(Int32.MaxValue);
        }
    
        private static void TimerTick(object sender, ElapsedEventArgs e)
        {
            LogMessage($"Timer Tick: timer.Enabled is {timer.Enabled}");
        }
    
        private static void LogMessage(string message)
        {
            Console.WriteLine($"{DateTime.Now:HH:mm:ss.fff} {message}");
        }
    }
    

    节目输出:

    19:46:23.043 Starting timer...
    19:46:24.068 Timer Tick: timer.Enabled is False
    19:46:26.053 Timer is stopped
    19:46:26.057 Setting AutoReset to true
    19:46:27.100 Timer Tick: timer.Enabled is False
    19:46:28.103 Timer Tick: timer.Enabled is False
    19:46:29.117 Timer Tick: timer.Enabled is False
    19:46:30.130 Timer Tick: timer.Enabled is False
    19:46:31.144 Timer Tick: timer.Enabled is False
    19:46:32.158 Timer Tick: timer.Enabled is False
    ...
    

1 个答案:

答案 0 :(得分:0)

根据MS

  

事件处理方法可能同时在一个线程上运行   另一个线程调用Stop方法或将Enabled属性设置为   假。这可能导致在之后引发Elapsed事件   计时器停止。 Stop方法的示例代码显示了一种方法   避免这种竞争条件。

     

即使SynchronizingObject不为null,也可能发生Elapsed事件   调用DisposeStop方法后或Enabled之后   属性已设置为false,因为信号提升了   Elapsed事件总是排队等待在线程池线程上执行。   解决这种竞争条件的一种方法是set a flag告诉你   Elapsed事件的事件处理程序到ignore subsequent events

来源:https://msdn.microsoft.com/en-us/library/system.timers.timer.elapsed(v=vs.110).aspx 在跟踪之后,使用System.Timers.Timer和防止不需要的events的推荐样本是第二个样本: https://msdn.microsoft.com/en-us/library/system.timers.timer.stop(v=vs.110).aspx

看来我之前的调查并不完全正确。我也不会说完全错。我认为我的大部分假设都是正确的,等待反馈以改善答案!

我的原始答案

a.k.a。下降到System.Timers.Timer的黑暗

我还没有证明这一点,我只研究了班级的结构。这很糟糕......

计时器未停止!已启用!=已停止

好吧,你现在从未真正致电timer.Stop(),是吗?就像鲍勃叔叔所说的那样,编程中的许多东西成对出现,例如调用StartStop。如果您不希望它对您的更改作出反应,您应该Stop计时器!

行为正确

我很少使用这个类,但行为似乎是设计的。我知道这听起来是矛盾的,但请阅读以下内容:)

查看参考来源,只要您拨打myTimer.Start(),每次更改属性(AutoResetIntervalEnabled)时,定时器都会做出反应一个名为UpdateTimer的方法,在新参数集合指示时生成后续事件触发。

  • 不是一个错误,但仍有点令人不安:System.Timers.Timer AutoReset false设置为Start()System.Threading.Timer之后发射一次,仍会有{{1} }} 四处闲逛。请参阅下面的说明。

但是有状态/显示错误!至少在我看来

  • 如果我们更改了第4步以更新Interval而生成tick,我会希望在调用Enabled==false时计时器为LogMessagefunction,已经是这样了。
  • AutoReset设置为true时,(步骤4)我希望Enabled成为true,正如您所指出的那样。Enabled并非如此。让我们深入挖掘!

状态与行为不一致的可能原因

  • 在Timer.cs类中,System.Threading.Timer setter负责为支持字段timer创建和部署false个实例。
    • 状态从true更改为timer会创建true
    • 状态从false更改为timer处置Enabled +其他一些魔法。
  • 使用Start属性的唯一方法是StopMyTimerCallback
  • enabledfalse时,用于触发事件的内部回调autoreset会将false支持字段设置为Enabled。从某种意义上说,它是谎言,因为它有意绕过timer属性以保持MyTimerCallback活着和踢。如果不是,则该类的行为将不会如上所述。
  • 我天真的想法认为“显示错误”修复程序会在enabled中,当自动设置为false时,对{1}​​}到false的设置进行类似检查。甚至可能enabled = autoreset。我需要在此之后长时间洗澡,然后去睡觉=))

来源:https://referencesource.microsoft.com/#System/services/timers/system/timers/Timer.cs,050a7911a328d317

我认为应该更新问题以更好地描述行为,这对我来说都是新闻:)