C# 系统计时器计时器严重不准确

C# 系统计时器计时器严重不准确,c#,multithreading,foreach,timer,parallel.foreach,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 s

我已经使用
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秒太多了。 通过对
Parallel.ForEach
方法使用
new ParallelOptions{maxdegreeofpparallelism=Environment.ProcessorCount-1}
,这不会发生,并且可以看到预期的15秒间隔

我是否必须确保每个活动计时器始终有一个可用的内核?似乎更奇怪,因为“保留”可能是我计算的宝贵资源


编辑:如Yuval所示,通过
ThreadPool设置池中固定的最小线程数。SetMinThreads
解决了该问题。我还尝试了
Parallel.ForEach
方法的
新并行选项{maxdegreeofpparallelism=Environment.ProcessorCount}
(因此在初始问题中没有
-1
),这也解决了问题。然而,我没有很好的解释为什么这些修改解决了这个问题。可能创建的线程太多,以致计时器线程“丢失”了“很长”一段时间,直到再次执行。

并行类使用称为自复制任务的内部TPL功能。它们意味着消耗所有可用的线程资源。我不知道什么样的限制是到位的,但它似乎是所有消耗。几天前,我基本上回答了同样的问题


Parallel
类很容易产生数量惊人的任务,而且没有任何限制。很容易激发它产生无限线程(每秒2个)。在没有手动指定的最大DOP的情况下,我认为<代码>并行< /代码>类不可用。它是一颗定时炸弹,在生产过程中,在负载下随机爆炸

Parallel
在许多请求共享一个线程池的ASP.NET场景中尤其有害

更新:我忘了指出关键点。计时器计时将排队到线程池。如果池已饱和,它们将排队并在稍后执行。(这就是计时器滴答声可以同时发生或在计时器停止后发生的原因。)这解释了您看到的情况。解决这个问题的方法是修复池重载

此特定场景的最佳修复方案是使用固定线程数的自定义任务调度程序<代码>并行可以使用该任务调度程序。并行扩展extra有这样一个调度器。从全局共享线程池中获取该工作。通常情况下,我会重新编译PLINQ,但这无法使用调度程序。从某种意义上说,Parallel和PLINQ都是不必要的残废api

不要使用
ThreadPool.SetMinThreads
。不要弄乱全局进程范围的设置。只需离开可怜的线程池

另外,不要使用
Environment.ProcessorCount-1
,因为这会浪费一个内核

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

计时器是操作系统内核中的一种数据结构。只有勾号必须排队时才有线程。不确定具体是如何工作的,但滴答声最终会排入.NET中的线程池。这就是问题的开始


作为一种解决方案,您可以启动一个在循环中休眠的线程来模拟计时器。不过,这是一种攻击,因为它无法修复根本原因:线程池过载。

如果在执行并行循环之前显式设置线程池中的线程数,这种情况还会发生吗?(使用
ThreadPool.SetMinThreads
)当您使用所有处理器时,是否会限制处理器的使用?如果是这样,由于windows处理消息泵的方式,所有事件的响应都会变慢。我怀疑运行并行线程的线程(可能是运行计时器的同一线程)正在阻塞自身。如果你能找到一种方法在不同的线程上运行你的计时器,你的问题可能会消失。运行计时器的线程可能会有所不同,具体取决于应用程序的托管方式。顺便说一句(例如Windows窗体vs console应用程序)@YuvalItzchakov,这似乎可行,谢谢。我已经将它设置为一个固定值(即使我只有4个内核,也尝试了50个),它可以工作。我还尝试设置了
新的并行选项{MaxDegreeOfParallelism=Environment.ProcessorCount}
(因此没有
-1
),效果也一样。问题解决了-请解释?为什么设置此选项可以解决问题,而不管该值设置为什么?