2013-07-02 23 views
3

线程池饿死我有类似下面的一些代码:预防与计时器在同一时间运行的Parallel.ForEach

long progress = 0; 

using (Timer timer = new Timer(state => { Console.Write(Interlocked.Read(ref progress); }, null, 5000, 5000) 
{ 
    Parallel.ForEach(list, item => 
    { 
    item.DoTask(); 
    Interlocked.Incrememt(ref progress); 
    } 
} 

我似乎遇到线程池饥饿,因为数字被写入控制台是偶尔写的 - 当然不是每5秒钟一次。在三个数字连续写入之前,可能会有15秒的长时间停顿。我猜计时器正在与Parallel.ForEach打交道来从线程池中获取一个线程来安排回调。

我该如何解决这个问题?

+0

DoTask当前正在执行什么工作? –

+0

使用实体框架的DbContext进行数据库处理 –

+0

我尝试用'Thread.Sleep()'模拟你的'DoTask()',我没有得到你描述的行为。你能否包含复制你的问题的短代码? – svick

回答

2

我似乎遇到线程池饥饿

饥饿可能是错误的心智模式。线程池调度程序的工作是将执行的线程数保持在由ThreadPool.SetMinThreads()设置的最小值。默认值等于机器上可用的处理器内核数量。允许更多并发运行通常是有害的,操作系统线程调度器将被迫在它们之间进行上下文切换,这需要花费时间。

如果那些tp线程实际上烧录核心,这是一个考虑因素。当他们进行“数据库处理”时,这在你的选择中是不太可能的。这通常会涉及很多死区时间,等待dbase引擎执行操作的线程。

你的Timer回调函数也是为了线程池而竞争的,它的回调函数在tp线程上运行。如果现有的线程在半秒内不完成,则tp调度程序允许另一个线程运行。

实际效果与您所描述的有关。你没有看到很多用于你的程序的cpu,并且“聚集”是一个明显的可能性,因为这是Parallel.ForEach()试图实现的。你想更多 tp线程可以同时运行,所以至少有一些可以做有意义的工作,而其他人正在等待dbase引擎。

无论你真的会得到相当可疑的结果,很可能它确实是限制程序的dbase引擎。网络带宽是下一个。接下来是一个不喜欢你超载他的服务器的滴答dbase管理员。你可以修改ThreadPool.SetMinThreads(),但不要期待奇迹。

请注意,您需要防止计时器回调的重新进入。当现有的Parallel.ForEach()循环尚未完成时让计时器打勾是非常糟糕的。

+0

你描述的所有这些问题,他们不会影响计时器的频率,不是? – svick

+0

Hans,计时器tick应该是报告Parallel.ForEach()循环有多少次迭代每5秒运行一次,所以当循环未完成时它是否会被打勾?这为什么会发生? –

+2

即使回拨被阻止,计时器也会打勾。这可能会导致多个数据库操作同时运行 –