我正在使用 System.Threading Timers 轮询不同线程上的传感器(由于通信延迟,一次一个很慢)。它还允许用户通过更改计时器周期来更改轮询速率。
我无法弄清楚的是,如果计时器在下一个周期之前没有完成会发生什么。我写了一个测试程序,但它并没有真正回答任何问题。
如果我有一个函数需要 ~1.7 秒才能运行并每 10 秒调用一次,它会在下一个函数开始之前完成,并且我的 CPU 使用率在 13%(一个核心 100%)和 0% 之间波动。
t = new Timer(doWork, null, 0, 10000);
private void doWork(object o)
{
for(int i = 0; i < numberItts; i++)
{
}
}
如果我然后将计时器周期降低到 1 秒,我希望它要么在前一个线程完成之前不执行线程,要么继续生成新线程,并且随着更多线程在其他完成之前开始,CPU 使用率会攀升。实际发生的是 CPU 使用率在 13% 到 25% 之间波动。
将周期更改为 500 毫秒,CPU 使用率将在 38% 到 50% 之间波动。当然,在这一点上,他们的开始应该比结束要快得多。
如何管理这些线程?当轮询速率快于线程可以完成的速率时,限制创建的数量是什么?
与 System.Windows.Forms.Timer
不同,System.Threading.Timer
使用线程池,如果计时器处理程序完成的时间超过计时器间隔,则不会受到阻止。
因此,如果您的 doWork
大约需要“~1.7 秒”才能完成,并且计时器间隔为一秒,则您希望看到多个并发线程进入 doWork
。
如何管理这些线程?当轮询速率快于线程可以完成的速率时,限制创建的数量是什么?
这一切都由 Timer
类和关联的线程池处理。
MSDN 是这样说的:
计时器执行的回调方法应该是可重入的,因为它是在 ThreadPool 线程上调用的。如果计时器间隔小于执行回调所需的时间,或者所有线程池线程都在使用中并且回调多次排队,则可以在两个线程池线程上同时执行回调。更多。。。
因此,给定这段代码,其中计时器间隔为 2 秒,处理程序处理时间为 1 秒,我们可以期望每次都使用相同的线程,因为重用同一线程通常比启动一个新线程更好:
class Program
{
static void Main(string[] args)
{
var t = new Timer(doWork, null, 0, 1000);
Console.WriteLine("Press any key to quit");
Console.ReadKey();
}
private static void doWork(object o)
{
Console.WriteLine("Thread: {0}", Environment.CurrentManagedThreadId);
// simulate lengthy process
Thread.Sleep(1000);
}
}
将间隔和处理时间更改为 1 秒,由于轻微重叠而导致随机线程。
将间隔更改为 200 毫秒并将处理时间保持在 1 秒会导致工作线程数比以前更多。原因是线程池已经意识到委托完成的时间比计时器间隔长,因此它试图跟上:
我认为在 1 秒的计时器周期内会发生以下情况:
即线程的处理速度仍然快于创建线程的速度。即使 500 毫秒也是如此。
我认为当您将计时器周期减少到一个值时,您期望的行为会发生