我正在使用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%之间波动.当然,在这一点上,他们应该比他们结束时更快地开始.
这些线程是如何管理的?当轮询速率快于完成线程的速率时,限制创建的数量是多少?
与使用线程池不同System.Windows.Forms.Timer
,如果您的计时器处理程序需要的时间超过完成的计时器间隔,则不会被阻塞.System.Threading.Timer
因此,如果您doWork
需要大约"~1.7s"来完成并且您的计时器间隔是一秒,那么您可能会看到多个并发线程进入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秒会导致工作线程数量比以前更多.原因是线程池已经意识到委托的完成时间比定时器间隔要长,所以它试图跟上: