【问题标题】:Timer randomly doesn't fire定时器随机不触发
【发布时间】:2014-07-17 19:13:05
【问题描述】:

我的代码看起来像这样

mTestModeMetadataTimer = new System.Threading.Timer(SomeTimerCallback, null, 1000, Timeout.Infinite);

Stopwatch tmStopwatch = new Stopwatch();

private void SomeTimerCallback(object state)
{
    // doing minimal work here

    Console.WriteLine("{0}: SomeTimerCallback time: {1}", System.Threading.Thread.CurrentThread.ManagedThreadId, tmStopwatch.ElapsedMilliseconds);
    tmStopwatch.Restart();

    // Better to be on the safe side and do this slightly more than once per second, than slightly less.
    mTestModeMetadataTimer.Change(990, Timeout.Infinite);
}

一切正常,只是偶尔会在此控制台输出中看到计时器事件之间存在巨大延迟。

 31: SomeTimerCallback time: 998
 21: SomeTimerCallback time: 997
 20: SomeTimerCallback time: 999
 3: SomeTimerCallback time: 989
 3: SomeTimerCallback time: 1000
 3: SomeTimerCallback time: 994
 37: SomeTimerCallback time: 999
 3: SomeTimerCallback time: 991
 29: SomeTimerCallback time: 1002
 37: SomeTimerCallback time: 1000
 3: SomeTimerCallback time: 17568
 3: SomeTimerCallback time: 999
 29: SomeTimerCallback time: 993

这是一个相当大的应用程序的一小部分。 System.Timers.Timer 存在相同的行为,事实上,在整个应用程序中的其他不同时间都会发生。我将线程 ID 添加到此特定计时器的控制台输出中,希望能更深入地了解为什么在正确的一秒事件中随机经过 17.5 秒的时间。

我明显做错了什么吗?也许我可以收集更多数据来弄清楚为什么我的计时器表现得很有趣?

如有任何建议,我们将不胜感激。

【问题讨论】:

  • 您是否在您的程序中做其他会导致线程池中大量线程正在使用的事情?
  • 有相当多的线程被抛出(不幸的是,我们被困在使用使用大量线程的第三方库中)。对于这个特定实例,应用程序有大约 250 个线程,总 CPU 使用率约为 11% (i7 4770)。

标签: c# .net timer system.timers.timer


【解决方案1】:

从您的comment 来看,发生这种情况的原因是您正在耗尽线程池中的线程。因为每个线程都在使用中,所以计时器必须等待 17.5 秒才能使线程可用,然后才能运行。

您的选择是增加线程池中的max number of threads 或使用不使用线程池的a different timerSystem.Timers.Timer 和不使用线程的synchronization object(仍然使用线程池来启动它)或带有消息泵运行的System.Windows.Forms.Timer

【讨论】:

  • System.Timers.Timer 使用线程池,即使设置了SynchronizingObject。回调最初在线程池线程上执行,然后在同步对象的线程上执行BeginInvoke。详情请见referencesource.microsoft.com/#System/services/timers/system/…
  • @JimMischel 不知道!
  • 刚刚使用 System.Threading.ThreadPool.GetMaxThreads() 检查了线程池的大小,它返回了 2047 个工作线程和 1000 个完成端口线程。我想我已经快要耗尽线程池了。
  • @Afcrowe 这是我能想到的唯一可以描述这种行为的东西。
  • 检查可用线程而不是最大线程:ThreadPool.GetAvailableThreads。这将为您提供更准确的信息,而不是假设您没有达到最大线程数。
【解决方案2】:

System.Threading.Timer 在从 CLR 线程池获得的工作线程上调用事件处理程序。如果线程池处于饥饿状态,回调可能会排队等待线程被释放。

要检查是否耗尽了线程池,可以在调试时定期记录可用线程的数量:

int workerThreads;
int completitionPortThreads;
System.Threading.ThreadPool.GetAvailableThreads(out workerThreads, out completitionPortThreads);

【讨论】:

    【解决方案3】:

    尝试在 Timer 构造函数中使用周期性:

    mTestModeMetadataTimer = new System.Threading.Timer(SomeTimerCallback, null, 1000, TimeSpan.FromMilliseconds(990));
    

    你确定你没有处于调试模式并且你在继续之前让时间过去了吗?

    【讨论】:

    • 绝对不是由断点或用户干扰引起的。我可以开始我的发布构建并离开十分钟,当我回来时,我看到这些漫长的等待时间在整个日志中乱七八糟。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2018-02-21
    • 1970-01-01
    • 2015-07-10
    • 1970-01-01
    • 2023-04-03
    • 1970-01-01
    • 2020-12-30
    相关资源
    最近更新 更多