Parallel.Foreach在幕后做了什么?

Posted

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Parallel.Foreach在幕后做了什么?相关的知识,希望对你有一定的参考价值。

所以我不能理解这里的概念。我有一个使用Parallel类和Foreach方法的方法。但我不明白的是,它是否创建新线程,以便它可以更快地运行该功能?

我们以此为例。我做一个正常的foreach循环。

private static void DoSimpleWork()
        {
            foreach (var item in collection)
            {
                //DoWork();
            }
        }

它将做的是,它将采取列表中的第一项,分配方法DoWork();到它并等到它完成。简单,朴实,有效。

现在..有三种情况我很好奇如果我这样做。

Parallel.ForEach(stringList, simpleString =>
            {
                DoMagic(simpleString);
            });

将Foreach拆分为4块大块吗?所以我认为正在发生的是它需要列表中的前4行,将每个字符串分配给每个“线程”(假设并行创建4个虚拟线程)完成工作,然后从该列表中的下一个4开始?如果这是错的,请纠正我,我真的想了解这是如何工作的。

然后我们有了这个。这基本上是相同的,但有一个新的参数

Parallel.ForEach(stringList, new ParallelOptions() { MaxDegreeOfParallelism = 32 }, simpleString =>
            {
                DoMagic(simpleString);
            });

我很好奇的是这个

new ParallelOptions() { MaxDegreeOfParallelism = 32 }

这是否意味着它将从该列表中获取前32个字符串(如果列表中有许多字符串)然后执行与我上面讨论的相同的事情?

而对于最后一个。

Task.Factory.StartNew(() =>
            {
                Parallel.ForEach(stringList, simpleString =>
                {
                    DoMagic(simpleString);
                });
            });

这会创建一个新任务,将每个“块”分配给它自己的任务吗?

答案

不要将异步代码与并行混合。任务是用于异步操作 - 查询数据库,读取文件,等待一些相对计算成本低廉的操作,以便您的UI不会被阻止和无响应。

平行是不同的。这是为1)多核系统和2)计算密集型操作而设计的。我不会详细介绍它是如何工作的,可以在MS文档中找到这种信息。简而言之,Parallel.For很可能会让自己决定究竟何时以及如何运行。它可能违反你的参数,即MaxDegreeOfParallelism或其他一些。整个想法是提供最佳的并行化,从而尽快完成您的操作。

另一答案

Parallel.ForEach执行相当于C#foreach循环,但每次迭代并行执行而不是顺序执行。没有排序,它取决于操作系统是否可以找到可用的线程,如果它将执行

MaxDegreeOfParallelism 

默认情况下,For和ForEach将使用OS提供的线程数,因此从默认值更改MaxDegreeOfParallelism仅限制应用程序将使用多少并发任务。

您通常不需要修改此参数,但可以选择在高级方案中更改它:

  1. 当您知道您正在使用的特定算法不会超出一定数量的核心时。您可以设置该属性以避免在其他核心上浪费周期。
  2. 当您同时运行多个算法并希望手动定义每个算法可以使用的系统数量时。
  3. 当线程池的启发式方法无法确定要使用的正确线程数时,最终可能会注入太多线程。例如在长时间运行的循环体迭代中,线程池可能无法区分合理进度或活锁或死锁,并且可能无法回收为提高性能而添加的线程。您可以设置该属性以确保不使用超过合理数量的线程。

当你需要对长期运行的计算绑定任务进行细粒度控制时,通常会使用Task.StartNew,就像@СергейБоголюбов所提到的那样,不要混淆它们

它创建一个新任务,该任务将异步创建线程以运行for循环

你可能会发现这本电子书很有用:http://www.albahari.com/threading/#_Introduction

另一答案

工作然后从该列表中的下一个4开始?

这取决于机器的硬件以及机器核心与CPU正在处理的其他进程/应用程序的繁忙程度

这是否意味着它将从该列表中获取前32个字符串(如果列表中有许多字符串),然后执行与上面讨论的相同的操作?

不,不能保证它会占用前32,可能会更少。每次执行相同的代码时都会有所不同

Task.Factory.StartNew创建一个新任务,但它不会像您期望的那样为每个块创建一个新任务。

将Parallel.ForEach放入新任务中将无助于您进一步缩短并行任务本身所需的时间。

以上是关于Parallel.Foreach在幕后做了什么?的主要内容,如果未能解决你的问题,请参考以下文章

为什么线程比Parallel.Foreach更快打开OracleConnection?

如何将此 foreach 代码转换为 Parallel.ForEach?

在 Parallel.foreach 中等待 [重复]

为啥要先执行 Return 语句而不是 Parallel。 ForEach [重复]

Parallel.ForEach() 与 foreach(IEnumerable<T>.AsParallel())

带有 BlockingCollection.GetConsumableEnumerable 的 Parallel.ForEach 循环