System.Timers.Timer大量不准确

时间:2015-09-18 18:54:36

标签: c# multithreading foreach timer parallel.foreach

我编写了一个使用Parallel.ForEach使用所有可用内核的程序。 ForEach的列表包含~1000个对象,每个对象的计算需要一些时间(~10秒)。 在这种情况下,我设置了一个这样的计时器:

timer = new System.Timers.Timer();
timer.Elapsed += TimerHandler;
timer.Interval = 15000;
timer.Enabled = true;

private void TimerHandler(object source, ElapsedEventArgs e)
{
        Console.WriteLine(DateTime.Now + ": Timer fired");
}

目前,TimerHandler方法是一个存根,以确保此方法不会导致问题。

我的期望是TimerHandler方法每15秒执行一次。但是,两次调用此方法之间的时间甚至达到40秒,因此25秒太多。 通过new ParallelOptions { MaxDegreeOfParallelism = Environment.ProcessorCount -1 }使用Parallel.ForEach方法,这不会发生,并且可以看到预期的15秒间隔。

我是否打算确保每个活动计时器始终有一个核心可用?似乎有点奇怪,因为"保留"可能是我计算的宝贵资源。

编辑:如Yuval所示,ThreadPool.SetMinThreads设置池中固定的最小线程数就解决了问题。我还尝试了new ParallelOptions { MaxDegreeOfParallelism = Environment.ProcessorCount }(因此在初始问题中没有-1Parallel.ForEach方法,这也解决了问题。但是,我没有很好的解释为什么这些修改解决了这个问题。也许有很多线程被创建,计时器线程刚刚失去"丢失"对于"长"再次执行的时间。

1 个答案:

答案 0 :(得分:1)

Parallel类使用称为自我复制任务的内部TPL工具。它们意味着消耗所有可用的线程资源。我不知道到底有什么样的限制,但似乎它非常耗费精力。我几天前回答了基本相同的问题。

Parallel类很容易产生疯狂的任务量而没有任何限制。它很容易激发它产生无限的线程(每秒2个)。我认为Parallel类没有手动指定的max-DOP就无法使用。它是一颗定时炸弹,在负载下随机爆炸。

Parallel在ASP.NET场景中特别有毒,其中许多请求共享一个线程池。

更新:我忘记了关键点。计时器滴答排队到线程池。如果池已经饱和,它们会排队并在以后执行。 (这就是为什么定时器滴答可以同时发生或定时器停止后发生的原因。)这解释了你所看到的。解决这个问题的方法是修复池重载。

此特定方案的最佳修复方法是具有固定线程数量的自定义任务计划程序。可以使Parallel使用该任务调度程序。 Parallel Extension Extras有这样的调度程序。从全局共享线程池中获取该工作。通常情况下,我会建议使用PLINQ,但是无法使用调度程序。从某种意义上说,Parallel和PLINQ都是不必要的瘫痪API。

不要使用ThreadPool.SetMinThreads。不要搞乱全球流程的设置。只留下可怜的线程池。

另外,请勿使用Environment.ProcessorCount -1因为这会浪费一个核心。

  

计时器已经在自己的线程中执行

计时器是OS内核中的数据结构。在必须排队等号之前没有线程。不知道究竟是如何工作的,但是tick最终排队到.NET中的线程池。问题开始时那是

作为一种解决方案,您可以启动一个在循环中休眠的线程,以便模拟一个计时器。但这是一个黑客攻击,因为它没有解决根本原因:重载的线程池。