我正在开发一个程序,该程序详尽地搜索大量排列并对每个排列进行测试,以根据每个项目符合条件的类别累积统计数据。
搜索输入是使用一种方法生成的,该方法返回带有yield return的IEnumerable,因此它不会立即累积整个数据集,而是生成它的消耗。
这对于小型搜索空间来说效果很好但是当我进入数千亿条记录来检查生成测试用例的过程时,几乎立即耗尽了我机器上的16GB内存,并开始使用页面错误来破坏计算机在任何处理开始之前。
似乎正在发生的事情是Parallel.ForEach贪婪地尝试在开始处理任何内容之前枚举整个输入集。
所以,我的问题是,如何限制Parallel.ForEach从我的测试用例生成器请求输入数据的速率?
我已经尝试在Samples for Parallel Programming with the .NET Framework中使用ChunkPartitioner了,可能ForEach在工作完全饱和线程池时可能会停止要求更多分区,但事实并非如此。
我试过在互联网上搜索一下Parallel.ForEach如何消耗输入以及我可以影响这个过程的方法的解释但除了分区输入集之外我找不到任何其他方法。
我刚接近这个问题错了吗?是否存在可以更好地解决此类问题的替代模式?
答案 0 :(得分:4)
我在这里看到两个(潜在的)问题。一个是默认的可枚举分区实际上是贪婪的(意味着它试图实现队列中前面的项目)。使用.NET 4.5中的EnumerablePartitionerOptions
很容易解决这个问题:
var partitioner = Partitioner.Create(
EnumerateItems(), // IEnumerable<T>
EnumerablePartitionerOptions.NoBuffering
);
Parallel.ForEach(partioner, new ParallelOptions { MaxDegreeOfParallelism = 4 }, i =>
{
// Process item.
});
第二个问题可能会显示自己,如果你的工作不受CPU限制,最终导致Parallel.ForEach
增加工作线程的数量,这可以通过指定MaxDegreeOfParallelism
来解决防止它使线程池饱和。