System.Threading.Timer的可伸缩性如何?
我正在编写一个需要使用Timer
s的应用程序,但其中可能有很多。 System.Threading.Timer类的可伸缩性如何?该文档仅说它是"轻量级的",而没有进一步解释。这些计时器是否陷入代表一个"计时器"处理所有回调的单个线程(或者很小的线程池)中,或者每个"计时器"都有自己的线程?
我猜想重新表达这个问题的另一种方法是:如何实现System.Threading.Timer?
解决方案
回答
我认为我们可能想重新考虑设计(也就是说,如果我们自己可以控制设计)。如果我们使用了太多计时器,实际上这是我们所关心的,那么那里显然存在合并的潜力。
这是几年前《 MSDN杂志》上的一篇好文章,比较了三个可用的计时器类,并对它们的实现提供了一些见解:
http://msdn.microsoft.com/zh-CN/magazine/cc164015.aspx
回答
我说这是为了回答许多问题:不要忘记该框架的(托管)源代码可用。我们可以使用此工具来获取全部信息:http://www.codeplex.com/NetMassDownloader
不幸的是,在这种特定情况下,很多实现都是在本机代码中进行的,因此我们不必去看它……
但是,他们肯定使用池线程,而不是按计时器线程。
实现大量计时器的标准方法(这是内核在内部进行操作的方式,我可能会间接怀疑大量计时器是如何结束的)是维护按时间到到期时间排序的列表,因此系统只需担心检查即将到期的下一个计时器,而不用担心整个列表。
粗略地讲,这为启动计时器提供了O(log n),为运行计时器提供了O(1)。
编辑:一直在寻找杰夫·里希特(Jeff Richter)的书。他说(对于Threading.Timer),它对所有Timer对象都使用一个线程,该线程知道下一个计时器(即如上所述)的到期时间,并在适当的时候调用ThreadPool.QueueUserWorkItem进行回调。这样的结果是,如果我们没有在计时器上完成对某个回调的服务,而后一个计时器到期,那么回调将在另一个池线程上重新输入。因此,总而言之,我怀疑我们会发现拥有大量计时器会遇到很大的问题,但是如果大量计时器在同一计时器处触发和/或者它们的回调运行缓慢,则可能会导致线程池耗尽。
回答
就像DannySmurf所说的:^巩固它们。创建一个计时器服务,并要求提供计时器。它只需要保留1个活动计时器(用于下一个到期调用)和所有计时器请求的历史记录,然后在AddTimer()/ RemoveTimer()上重新计算该时间即可。
回答
Consolidate them. Create a timer service and ask that for the timers. It will only need to keep 1 active timer (for the next due call)...
为了使它比仅创建许多Threading.Timer对象更好,我们必须假定它与Threading.Timer在内部已经不完全一样。我很想知道我们是如何得出这个结论的(我还没有分解框架的本机部分,所以我们很可能是正确的)。