计时器随机不触发。

4
我有这样的代码:

我有这样的代码

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秒经过时间的更深入的见解。
我是否明显做错了什么?也许有一些更多的数据我可以收集,以弄清我的计时器为什么这样操作?
任何建议都将不胜感激。

1
你的程序中是否有其他操作会导致线程池中使用大量线程? - Scott Chamberlain
有相当多的线程被抛出来(不幸的是,我们被困在使用一个使用了荒谬数量线程的第三方库中)。对于这个特定的情况,应用程序大约有250个线程,总CPU使用率约为11%(i7 4770)。 - Afcrowe
3个回答

2

System.Threading.Timer 会从CLR线程池中获取一个工作线程,并在该线程上调用事件处理程序。如果线程池处于饥饿状态,则可能会将回调排队,直到线程被释放。

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

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

2
根据您的评论,发生这种情况的原因是您耗尽了线程池中的线程。因为每个线程都在使用中,计时器必须等待17.5秒才能运行,直到有一个线程可用。
我认为您有两个选择:要么增加线程池中的最大线程数,要么使用不使用线程池的其他计时器System.Timers.Timer与不使用线程池的同步对象(仍然使用线程池来启动),或者使用带消息泵的System.Windows.Forms.Timer)。

1
即使设置了“SynchronizingObject”,System.Timers.Timer 仍使用线程池。回调最初在线程池线程上执行,然后在同步对象的线程上执行 BeginInvoke。有关详细信息,请参见 http://referencesource.microsoft.com/#System/services/timers/system/timers/Timer.cs#897683f27faba082。 - Jim Mischel
@JimMischel 不知道那个! - Scott Chamberlain
刚刚使用 System.Threading.ThreadPool.GetMaxThreads() 检查了线程池的大小,结果显示有 2047 个工作线程和 1000 个完成端口线程。我认为我远未达到耗尽线程池的点。 - Afcrowe
@Afcrowe 这是我能想到的唯一可以描述那种行为的事情。 - Scott Chamberlain
检查可用线程而不是最大线程数:ThreadPool.GetAvailableThreads。这将为您提供更准确的信息,而不是假设您没有达到最大线程数。 - Edin

0
尝试在Timer构造函数中使用周期性参数:
mTestModeMetadataTimer = new System.Threading.Timer(SomeTimerCallback, null, 1000, TimeSpan.FromMilliseconds(990));

你确定你不是在调试模式下,然后让时间流逝了才继续吗?


绝对不是由于断点或用户干扰引起的。我可以启动我的发布构建,然后离开十分钟,当我回来时,我会看到这些漫长的等待时间散布在日志中。 - Afcrowe

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接