【问题标题】:Are c# timers naturally multithreaded?c#定时器自然是多线程的吗?
【发布时间】:2011-09-14 10:28:48
【问题描述】:

如果我有两个系统计时器分别在 10 秒和 20 秒触发事件,那么对事件函数的调用是多线程的吗?在下面的场景中,我有分别以 10 秒和 12 秒间隔触发事件的计时器。函数 'My10sEvent' 应该首先被调用。如果是慢速函数,需要 8 秒才能运行,是会阻塞另一个事件(tmr12s),还是会在 12s 准时触发第二个事件?

System.Timers.Timer tmr10s = new System.Timers.Timer(10000.0);
tmr10s.Enabled = true;
tmr10s.Elapsed += new ElapsedEventHandler(My10sEvent);

System.Timers.Timer tmr12s = new System.Timers.Timer(12000.0);
tmr12s.Enabled = true;
tmr12s.Elapsed += new ElapsedEventHandler(My12sEvent);

Thread.Sleep(System.Threading.Timeout.Infinite); //sleep indefinitely to let the above events fire

【问题讨论】:

  • 我不认为它会阻塞.. 反正我不是粗鲁,但你自己检查真的那么难吗?
  • 不,你一点也不粗鲁。我确实可以自己检查(并且正在这样做),但我发现这里的社区除了简单的答案外,还经常提供精彩的见解:)
  • 是的,这是一个有效的观点,我理解。有时我只是觉得人们使用 SO 当普通的旧 google 或 C# 编译器就足够了,而你的问题的措辞方式(基本上,是或否)使它看起来像这些“懒得启动 VS”问题之一

标签: c# multithreading timer


【解决方案1】:

CLR 使用专用线程来跟踪活动的 System.Timers.Timer 和 System.Threading.Timer 计时器。 Elapsed 事件在从线程池中拉出的另一个线程上引发。

所以是的,它们一直在滴答作响,不会相互影响。您必须非常小心,您的 Elapsed 事件处理程序很有可能在它仍在执行时再次被调用。当花费的时间超过间隔时会发生这种情况。或者更糟的是,当机器负载很重或者你有很多活动的线程池线程时。如果您的事件处理程序不是线程安全的,这可能会导致很难诊断故障。几乎从来没有。将计时器的 AutoReset 属性设置为 false 是避免此问题的一种简单方法。

【讨论】:

  • 为什么CLR需要专用线程?你能解释一下吗?
  • 因为它需要知道何时启动线程池线程来调用 Elapsed 事件。这些事情不会自动发生。逻辑很简单,它只是在列表中扫描下一个到期的计时器并休眠那么长时间。
  • AFAIK 有内核计时器对象,CLR 的功能基于它。而且该内核对象当然不需要单独的线程来等待。当然,我不能确定 CLR 是如何在内部实现的,但是只有一个线程来接收定时器的通知是一种浪费。
  • 可以在SSCLI20源码中看到实现。仍然可以这样工作,您可以看到线程在混合模式下使用调试器启动。
  • 好吧,我找到了名为“.NET SystemEvents”的线程并检查了来源——它本质上是为 Windows 消息服务的 WndProc 线程。第二个线程可能是 GC 和我正在执行的主线程。检查本机线程/堆栈也没有显示任何包含等待单个下一个计时器的内容。如果我错过了什么——对不起,我在低级本机调试方面不是很熟练。您能否指出该线程的创建和逻辑所在的 SSCLI 中的位置?
【解决方案2】:

如果您使用 System.Threading.Timer 或在没有 SynchronizingObject 的情况下调用 System.Timers.Timer,它看起来不会阻塞。

类似问题:Do C# Timers elapse on a separate thread?

【解决方案3】:

来自System.Timers.Timer 文档:

基于服务器的计时器设计用于多线程环境中的工作线程。服务器计时器可以在线程之间移动以处理引发的 Elapsed 事件,从而在按时引发事件方面比 Windows 计时器更准确。

是的,是的。

此外,来自System.Timers.Timer.SynchronizingObject 属性文档:

当 SynchronizingObject 为 null 时,将在系统线程池中的线程上调用处理 Elapsed 事件的方法。有关系统线程池的更多信息,请参阅 ThreadPool。

所以除非设置了 SynchronizingObject,否则事件会在线程池线程上引发。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多