Stephen Toub的书第88页
http://www.microsoft.com/download/en/details.aspx?id=19222
有代码
private BlockingCollection<T> _streamingData = new BlockingCollection<T>();
// Parallel.ForEach
Parallel.ForEach(_streamingData.GetConsumingEnumerable(),
item => Process(item));
// PLINQ
var q = from item in _streamingData.GetConsumingEnumerable().AsParallel()
...
select item;
然后斯蒂芬提到
“时 将调用GetConsumingEnumerable作为数据源的结果传递给Parallel.ForEach,使用的线程 当集合变空时,循环有可能阻塞。并且Parallel.ForEach可能无法将阻塞的线程释放回ThreadPool以用于退役或其他用途。因此,使用如图所示的代码 如上所述,如果集合为空的任何时间段,则该过程中的线程数可能会稳定 生长;“
我不明白为什么线程数会增长?
如果集合为空,那么blockingcollection不会请求任何其他线程吗?
因此,您不需要使用WithDegreeOfParallelism来限制BlockingCollection上使用的线程数
答案 0 :(得分:3)
线程池有一个爬山算法,用于估计适当的线程数。只要添加线程增加了吞吐量,线程池就会创建更多线程。它会假设发生了一些阻塞或IO,并试图通过超过系统中处理器的数量来使CPU饱和。
这就是在线程池线程上执行IO和阻塞内容可能很危险的原因。
以下是所述行为的完整工作示例:
BlockingCollection<string> _streamingData = new BlockingCollection<string>();
Task.Factory.StartNew(() =>
{
for (int i = 0; i < 100; i++)
{
_streamingData.Add(i.ToString());
Thread.Sleep(100);
}
});
new Thread(() =>
{
while (true)
{
Thread.Sleep(1000);
Console.WriteLine("Thread count: " + Process.GetCurrentProcess().Threads.Count);
}
}).Start();
Parallel.ForEach(_streamingData.GetConsumingEnumerable(), item =>
{
});
我不知道为什么线程计数不断攀升,尽管它不会增加吞吐量。根据我解释的模型,它不会增长。但我不知道我的模型是否真的正确。
也许线程池有一个额外的启发式,如果它看不到任何进展(在每秒完成的任务中测量),它就会产生线程。这是有道理的,因为这可能会阻止应用程序中的大量死锁。如果重要任务无法运行,则会发生死锁,因为它们正在等待现有任务退出并使线程可用。这是线程池的一个众所周知的问题。