C# 流式数据块采集

C# 流式数据块采集,c#,streaming,blockingcollection,C#,Streaming,Blockingcollection,在Stephen Toub的书的第88页 这是密码 private BlockingCollection<T> _streamingData = new BlockingCollection<T>(); // Parallel.ForEach Parallel.ForEach(_streamingData.GetConsumingEnumerable(), item => Process(item)); // PLINQ var q = from item in

在Stephen Toub的书的第88页

这是密码

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;
private BlockingCollection\u streamingData=new BlockingCollection();
//并行ForEach
Parallel.ForEach(_streamingData.getconsumineGenumerable(),
项目=>过程(项目));
//普林克
var q=来自_streamingData.getconsumineGenumerable().aspallel()中的项
...
选择项目;
斯蒂芬接着提到

“什么时候 将调用GetConsumingEnumerable作为数据源的结果传递给Parallel.ForEach,该线程由 当集合变为空时,循环可能会阻塞。被阻塞的线程可能不会被Parallel.ForEach释放回线程池以供退役或其他使用。因此,代码如下所示 如上所述,如果有任何时间段集合为空,则进程中的线程计数可能会稳定 成长

我不明白线程数为什么会增长

如果集合为空,那么blockingcollection不会请求任何其他线程吗


因此,您不需要使用degreeofparallelism来限制BlockingCollection上使用的线程数

线程池有一个爬山算法,用于估计适当的线程数。只要添加线程增加吞吐量,线程池就会创建更多线程。它将假设发生了一些阻塞或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 =>
            {
            });
BlockingCollection\u streamingData=new BlockingCollection();
Task.Factory.StartNew(()=>
{
对于(int i=0;i<100;i++)
{
_streamingData.Add(i.ToString());
睡眠(100);
}
});
新线程(()=>
{
while(true)
{
睡眠(1000);
WriteLine(“线程计数:+Process.GetCurrentProcess().Threads.count”);
}
}).Start();
Parallel.ForEach(_streamingData.getconsumineGenumerable(),item=>
{
});
我不知道为什么线程数一直在增加,尽管它并没有增加吞吐量。根据我解释的模型,它不会增长。但我不知道我的模型是否正确


也许线程池有一个额外的启发式方法,如果它看不到任何进展(以每秒完成的任务数衡量),它会生成线程。这是有道理的,因为这可能会防止应用程序中出现大量死锁。如果重要任务由于等待现有任务退出并使线程可用而无法运行,则会发生死锁。这是线程池的一个众所周知的问题。

但是当出现阻塞时,为什么会使CPU饱和?例如:现代SSD有多个内部IO路径。我的有4个。当您有2个内核时,仍然需要4个线程来饱和SSD。线程池算法实际上会尝试创建4个线程(至少如果它工作正常的话)。它将生成线程,直到创建了5个。然后,它会注意到第5个线程并没有增加吞吐量,所以它会使第5个线程失效。这个过程的细节没有记录。但是当我的任务执行IO时,我经常在实践中看到它。很抱歉,如果线程没有提高吞吐量(比如被阻塞),我会感到困惑,那么在Stephen Toub的示例中为什么会出现这个问题?这里唯一的区别是阻塞线程不能退出吗?但是为什么会增加线程数呢?我补充了很多解释。我确信我的猜测是正确的。