c# - Parallel.ForEach挂起一个大循环

标签 c# multithreading foreach task-parallel-library enterprise-architect

我有一个使用TPL并行化的for循环实现。我正在使用配备4GB RAM和i3 Core处理器的Dell笔记本电脑。我有多个parallel.foreach,它们是使用Parallel.invoke调用的。该程序是Enterprise Architect的插件,用于在EA中创建模型图和对象。

代码是这样的:

Parallel.invoke(()=>parent1Creation(),()=>parent2Creation(),...);

每个父项创建都是一个Parallel.foreach:
Parallel.foreach(parents, (parent) => {
    //create parent 
    //create children
    for(child in parent.children) {
        childecreation();
    }

    for(child2 in parent.children) {
        childecreation();
    }
    //can be any type and number of children
} 

我遇到一个问题,当我的循环大小增加(即大约1500-2000次迭代)时,Enterprise Architect停止工作。

这是因为我的笔记本电脑配置,还是我使用并行循环或与Enterprise Architect一起使用的方式而引起的问题。

我该如何解决。

最佳答案

我不建议这种策略。一次运行许多Parallel.ForEach循环并不一定会提高性能(请参阅后面的警告),尤其是在每个Parallel.ForEach循环正在处理大量迭代的情况下。在某些时候,使用额外的线程将不再对您的性能有所帮助,而只会增加开销。

需要注意的是,Parallel.ForEach通常可以为特定的foreach循环选择最佳线程数(但并不完美)。没有明确保证特定的foreach循环将使用多少个线程(甚至不会并行运行),因此可以想象多个Parallel.ForEach循环实际上可以提高性能。最好的检查方法是使用调试器查看在给定点实际使用了多少个线程。如果不是您所期望的,则可以检查Parallel.ForEach循环中代码的实现(例如);此时您还可以采取其他步骤来尝试提高性能(例如,针对IO绑定(bind)和其他非CPU绑定(bind)操作的良好async/await实现,以便线程可以执行更多工作-参见下文)。

一个简单的例子:假设您有一个系统,其中有4个线程和4个内核,而这4个线程是系统上运行的唯一对象。 (显然,这永远不会发生)。从调度的角度来看,明智的做法是让每个内核分别处理一个线程。假设每个线程一直在忙(即从不等待),那么如何添加更多线程来提高性能呢?例如,如果您开始运行6个线程,那么显然至少一个内核现在必须至少运行2个线程,这增加了额外的开销,而没有明显的好处。这里的简化(可能是不正确的)假设是您的任务受100%CPU限制,并且线程实际上在单独的内核上运行。如果这些假设之一是不正确的,那显然是一个增强的机会。例如,如果一个线程花费大量时间等待IO绑定(bind)操作的结果,则CPU上的多个线程实际上可以提高性能。您也可以考虑使用异步/等待实现来提高性能。

关键是,在某个时候添加额外的线程不会给您带来任何性能上的好处,只是增加了开销(例如,如果所涉及的任务主要是CPU约束的,而不是主要是IO约束的,则尤其如此)。无法绕开这个事实。

非CPU绑定(bind)操作(例如,对服务器的调用等IO绑定(bind)任务)在主保持等待来自CPU/内存外部的结果的情况下,以不同的方式并行化。实际上,异步/等待并不一定会创建新线程。它的主要行为之一是将控制权返回给所讨论的方法的调用者,并在可能的情况下“尝试”在同一线程上执行其他工作。

重复我最喜欢的类比,假设您是10人一组的一部分出去吃饭。当服务员来点菜时,服务员要点菜的第一个家伙还没准备好,而其他九个人已经准备好了。服务员要做的正确的事情是,而不是等待第一个家伙准备好点菜,而是让其他9个人先点菜,然后如果他准备好了,然后再让第一个家伙点菜。他绝对不会请第二位服务员来等待那个家伙准备好。在这种情况下,第二个服务员可能实际上不会减少完成订单所需的总时间。这基本上就是异步/等待尝试完成的任务;例如,如果所有操作都在等待服务器的结果,那么理想情况下,您可以在等待服务器时执行其他操作。

另一方面,要扩大类比,服务员实际上不是自己做饭,绝对不是这种情况。在那种情况下,增加更多的人(以类推,线程)确实可以加快工作速度。

进一步推论,如果所有厨房都只有一个四头炉子,那么在他们遇到炉灶大小所施加的严格限制之前,可以增加多少人就存在一个严格的限制。一旦达到这个极限,实际上更多的厨房工作人员就会放慢速度,因为他们会互相妨碍,因为实际上一次 cooking 的食物数量有一个严格的限制。不管您的厨房员工多大,都不可能一次在炉子上 cooking 超过4种食物。在这种情况下,您拥有的核心数就和厨房的大小一样。一旦达到某个点,增加更多的厨房工作人员(线程)将损害您的绩效(而不是提高绩效)。

关于c# - Parallel.ForEach挂起一个大循环,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/39443578/

相关文章:

javascript - 我可以根据数组中的每个项目来操作 DOM

c# - 路线 VS 路线基地

c# - 在 Android 设备上开发 C#/.NET

multithreading - 有自动并行的序言实现吗?

android - API 级别 15 的 SurfaceView 线程中出现意外的 NullPointerException

C# list.remove 在 foreach 循环中抛出枚举操作错误

c# - 数据集操作

c# - 为什么 LINQ (c#) 与 Seq (f#) 之间存在性能差异

python - 如何在Python线程中使用qtwebkit?

c:foreach 标签不渲染