【问题标题】:Stopped System.Timers.Timer starts firing Elapsed event after setting AutoReset停止 System.Timers.Timer 在设置 AutoReset 后开始触发 Elapsed 事件
【发布时间】:2018-03-05 16:55:57
【问题描述】:

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

  1. 定时器的创建间隔为 1 秒,AutoReset 设置为 false
  2. 定时器启动,Elapsed 事件在 1 秒内触发。内部事件处理程序 timer.Enabledfalse,这是预期的,因为 AutoReset 已设置为 false
  3. 然后不会触发 Elapsed 事件,这是由于计时器停止而预期的。
  4. 如果现在我们将timer.AutoReset 设置为trueElapsed 事件开始每 1 秒触发一次。 timer.Enabled 仍然是错误的。

这是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
...

【问题讨论】:

  • 这是一个在很多方面都存在严重缺陷的课程。关于这个类是如何发生的,必须有一个很好的战争故事,我猜是一个可用性研究表明没有人能弄清楚如何使用 System.Threading.Timer。但是,是的,这与其代码的编写方式是一致的。你通常总是,总是将 Enabled 属性设置回 true 并且在 Elapsed 中将 AutoReset 翻转为 true 是没有意义的。

标签: c# .net timer


【解决方案1】:

根据 MS

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

即使SynchronizingObject 不为空,也可能发生已逝事件 在调用 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

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

我原来的答案

又名。下降到 System.Timers.Timer 的黑暗

我还没有证明任何这些,我只是研究了类的结构。不好……

计时器没有停止!启用!= 停止

好吧,你现在从来没有真正打电话给timer.Stop(),是吗?正如鲍勃叔叔所说,编程中的许多事情都是成对出现的,例如调用StartStop。如果您不希望计时器对您的更改做出反应,您应该 Stop 计时器!

行为正确

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

查看参考源,只要调用myTimer.Start(),每次更改属性(AutoReset,Interval,Enabled),计时器都会通过一个名为UpdateTimer的方法做出反应,生成后续事件会根据新参数 ensemble 的指示触发。

  • 不是错误,但仍然有点令人不安:System.Timers.TimerAutoReset 设置为 false,在 Start() 并触发一次之后,仍然会有一个 System.Threading.Timer 漫游。请参阅下面的说明。

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

  • 如果我们将第 4 步更改为更新 Interval,这将生成 tick,我希望计时器在调用 LogMessagefunction 时具有Enabled==false,情况已经如此。
  • AutoReset 设置为true 时,(第4 步)我希望Enabled 变为true,正如您所指出的,情况并非如此。让我们深入挖掘!

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

  • 在 Timer.cs 类中,Enabled 设置器负责为支持字段 timer 创建和处置 System.Threading.Timer 实例。
    • 状态从false 更改为true 创建timer
    • 状态从 true 更改为 false disposestimer + 其他一些魔法。
  • 使用Enabled 属性的唯一方法是StartStop
  • autoresetfalse 时,用于触发事件的内部回调MyTimerCallbackenabled 支持字段设置为false。从某种意义上说,它是在撒谎,因为它故意绕过Enabled 属性以保持timer 的活力和活力。如果不是,则该类的行为将与上述不同。
  • 我天真地认为“显示错误”修复将在MyTimerCallback 中,当自动重置为false 时,与设置enabledfalse 的检查类似。甚至可能是enabled = autoreset。之后我需要洗个澡,然后去睡觉 =))

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

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

【讨论】:

  • 如果我明确停止计时器,问题也会重现。您可以通过在Thread.Sleep(3000) 之后添加timer.Stop() 来检查它。
  • @CodeFuller 我想睡觉 :((((
  • @CodeFuller 我现在要睡觉了,明天是艰难的一天。我不知道为什么这门课曾经出现过,它似乎有很大的缺陷。我将不得不以比现在更好的心态重新评估我的假设,但我怀疑时间是否值得:) 我理解这种好奇心,我可能会从纯粹的“为什么”-itis...但是,仍然可以确保噩梦!最后一件有趣的事情:只有在使用 .Stop() 停止计时器后才会触发第一个 Interval 更改。
  • @CodeFuller 这是骗子吗? stackoverflow.com/a/18280560/8695782我真的要睡觉了
  • 似乎问题不在竞争状态或在时间停止之前排队的事件。在第一个计时器滴答之后和设置 AutoReset 之前有 2 秒的时间跨度,并且在此间隔期间不会触发任何事件。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2016-11-16
  • 2023-04-07
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多