我正在使用System.Threading计时器来轮询不同线程上的传感器(由于通信延迟,一次一个很慢)。它还允许用户通过更改计时器周期来更改轮询速率。
如果计时器在下一个时段之前没有完成,那么我无法解决的问题是什么。我写了一个测试程序,但它没有真正回答任何问题。
如果我有一个需要大约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%之间波动。当然,在这一点上,他们应该比他们结束时更快地开始。
如何管理这些线程?当轮询速率快于完成线程的速率时,限制创建的数量是什么?
答案 0 :(得分:7)
与System.Windows.Forms.Timer
不同,System.Threading.Timer
使用线程池,如果您的计时器处理程序需要的时间超过计时器间隔,则不会被阻止。
因此,如果您的doWork
需要“~1.7s”来完成并且您的计时器间隔为一秒,那么您可能会看到多个并发线程进入doWork
。
如何管理这些线程?当轮询速率快于完成线程的速率时,限制创建的数量是什么?
这全部由Timer
类和关联的线程池处理。
MSDN有这样的说法:
计时器执行的回调方法应该是可重入的,因为它是在ThreadPool线程上调用的。如果定时器间隔小于执行回调所需的时间,或者所有线程池线程都在使用且回调多次排队,则可以在两个线程池线程上同时执行回调。 more...
所以给定这段代码,其中定时器间隔为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 :(得分:2)
我认为以下1秒的计时器周期会发生以下情况:
即。仍然会比创建线程更快地处理线程。即使是500毫秒也是如此。
我认为当您将计时器周期缩短为某个值时,您会发生预期的行为。 1.7s / 8 = ~0.2125s(假设您有8个处理器可用,除了处理你的线程需要1.7秒。)