C#在核心之间有效地划分任务

C#在核心之间有效地划分任务,c#,.net,multithreading,parallel-processing,C#,.net,Multithreading,Parallel Processing,我正在做一个小的模拟,它运行在我的8核工作站上。仿真包括对大量独立节点之间的交互进行建模。在一个阶段中,我需要并行地对每个节点执行一系列简单的原子操作。我一直在使用System.Threading.Tasks中的Parallel.ForEach将操作并发应用于所有节点列表中的每个节点 这对于我用于测试的100-500个节点来说效果很好。负载平衡得很好,所有磁芯都在不断利用。不幸的是,当我尝试使用主数据集(5000多个节点)运行模拟时,一切都出错了。所有8个内核大部分时间都处于空闲状态,每几秒钟就

我正在做一个小的模拟,它运行在我的8核工作站上。仿真包括对大量独立节点之间的交互进行建模。在一个阶段中,我需要并行地对每个节点执行一系列简单的原子操作。我一直在使用System.Threading.Tasks中的Parallel.ForEach将操作并发应用于所有节点列表中的每个节点

这对于我用于测试的100-500个节点来说效果很好。负载平衡得很好,所有磁芯都在不断利用。不幸的是,当我尝试使用主数据集(5000多个节点)运行模拟时,一切都出错了。所有8个内核大部分时间都处于空闲状态,每几秒钟就达到100%,然后恢复到1%的利用率。几分钟后,抛出OutOfMemoryException,程序崩溃

我不完全确定哪里出了问题,但仍然怀疑我当前的代码产生的线程比任务的最佳线程多得多。我认为理想的方法是模型检测可用内核的数量N,将节点列表划分为N个段,然后生成N个线程,给每个线程一个单独的列表分区

我想问的是,这是否真的是一个很好的解决方案,是否存在更好的解决方案,以及它应该如何在C#中实现?欢迎提出任何建议或意见

编辑:按请求编写示例代码

Parallel.ForEach(listOfNodes, tempNode =>
{
   tempNode.foo();
} );

<snip>

void foo()
{
   foreach(myType bar in listOfmyType)
   {
       if (bar.isActive)
           setNodeActive();
   }
} 
Parallel.ForEach(listOfNodes,tempNode=>
{
tempNode.foo();
} );
void foo()
{
foreach(listOfmyType中的myType栏)
{
如果(bar.isActive)
setNodeActive();
}
} 
我认为理想的方法是 该模型能够检测出被测对象的数量 可用内核N,分区列表 将节点划分为N个段,然后生成N个 线程,给每个线程一个单独的 列表的分区

这正是Parallel.ForEach所做的,所以肯定还有另一个问题


你自己很难想出一个更好的(线程管理)系统。但是您可以在任务库中使用自定义调度程序

请参阅此线程,其中讨论如何限制
并行线程的数量。对于
用于避免内存不足的线程:


我会尝试将设置为大约500,然后看看会发生什么。

一个简短但完整的代码示例,演示这个问题会很有帮助,否则您可能得到的只是猜测;如果反复处理交换文件,CPU利用率会显著下降,因为CPU大部分时间都在等待磁盘。因此可以解释机器的行为;解决办法是减少内存使用。LBushkin,我考虑过,但是代码太通用了,似乎没有什么帮助。它只是:Parallel.ForEach(listOfNodes,tempnode=>{tempnode.foo()});
tempnode.foo()
看起来像什么?如果您使用普通的foreach,您是否仍然会遇到outofmemory错误(显然,它将只使用一个核心…),但这可能会指向
foo()中的一个问题。
只是一种预感,但由于您正在对交互进行建模,您是否有任何数据结构正在增长为O(N^2)?仅仅由于与每个节点的数据相关联的开销,您的内存耗尽速度可能比您想象的要快。500似乎仍然非常高,您能解释一下吗?我的估计是10-20(8核,很少或没有I/O)。是的,我认为8或10将是一个很好的限制。为什么500是一个很好的初始估计值?因为你说过它可以处理500个节点。它不必是500。我有一个Windows服务,它在网站后面的后台执行长时间运行的任务。它使用
ThreadPool
对象。我对它进行了一些测试,发现同时执行线程的理想数量是机器中的内核数量,所以YMMV.啊,这是有意义的。谢谢是的,对于纯CPU限制的系统来说,理想情况是运行N个线程(N=#个内核)。但您需要一些空间来补偿等待(I/O或锁)线程。所以8+是你的起点。别忘了,1个线程=1MB