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


180

我有些困惑Parallel.ForEach
Parallel.ForEach什么,它的作用是什么?
请不要引用任何MSDN链接。

这是一个简单的例子:

string[] lines = File.ReadAllLines(txtProxyListPath.Text);
List<string> list_lines = new List<string>(lines);

foreach (string line in list_lines)
{
    //My Stuff
}

如何用重写这个示例Parallel.ForEach


这可能已经在这里找到答案stackoverflow.com/questions/3789998/...
Ujjwal Manandhar

1
@UjjwalManandhar这实际上是完全不同的,因为它询问的是Parallel类与使用PLINQ 之间的区别。
Reed Copsey 2012年

18
其他人回答了如何重写。那怎么办呢?就像常规一样,它对集合中的每个项目都执行“操作” foreach。区别在于并行版本可以同时执行许多“操作”。在大多数情况下(取决于运行代码的计算机,繁忙程度以及其他因素),它会更快,这是最重要的优势。请注意,当您并行执行此操作时,您将无法知道以什么顺序处理这些项目。使用通常的(序列)foreach,可以保证lines[0]首先出现,然后依次lines[1]类推。
杰普·斯蒂格·尼尔森

1
@JeppeStigNielsen并不会总是更快,因为并行处理会产生大量开销。这取决于您要迭代的集合的大小以及其中的操作。正确的做法是实际测量使用Parallel.ForEach()与使用foreach()之间的差异。通常,普通的foreach()速度更快。
戴夫·布莱克

3
@DaveBlack当然。在每种情况下,都必须测量它是快还是慢。我只是在试图描述并行化。
Jeppe Stig Nielsen

Answers:


126
string[] lines = File.ReadAllLines(txtProxyListPath.Text);
List<string> list_lines = new List<string>(lines);
Parallel.ForEach(list_lines, line =>
{
    //Your stuff
});

6
只是想指出一点(对OP来说更多),这样就不会有误解,认为它只适用于List<T>;)
Reed Copsey

1
感谢您的关注和回答。我在代码中使用List <string>是因为使用HASH列表删除了重复项。使用常规数组,我们无法轻松删除重复项:)。
SilverLight'9

119
我很困惑,这个答案被标记为正确的答案,因为没有原始帖子问题的解释“什么是Parallel.ForEach以及它的作用是什么?” ...
fose 2015年

6
@fosb问题是问题标题已被编辑以完全改变其含义...因此该答案不再有意义。话虽如此,但这仍然是一个很差的答案
aw04

273

Foreach循环:

  • 迭代是一个接一个地进行的
  • foreach循环从单个线程运行。
  • 在.NET的每个框架中都定义了foreach循环
  • 慢速进程的执行可能会更慢,因为它们是顺序运行的
    • 进程2直到1完成后才能开始。流程3在2&1完成之前无法启动...
  • 快速进程的执行可以更快,因为没有线程开销

并行ForEach:

  • 执行以并行方式进行。
  • Parallel.ForEach使用多个线程。
  • .Net 4.0及更高版本的框架中定义了Parallel.ForEach。
  • 慢速进程的执行可以更快,因为它们可以并行运行
    • 进程1、2和3 可以同时运行(请参见下面示例中的重用线程)
  • 由于额外的线程开销,快速进程的执行可能会变慢

以下示例清楚地说明了传统的foreach循环与

Parallel.ForEach()示例

using System;
using System.Diagnostics;
using System.Threading;
using System.Threading.Tasks;
namespace ParallelForEachExample
{
    class Program
    {
        static void Main()
        {
            string[] colors = {
                                  "1. Red",
                                  "2. Green",
                                  "3. Blue",
                                  "4. Yellow",
                                  "5. White",
                                  "6. Black",
                                  "7. Violet",
                                  "8. Brown",
                                  "9. Orange",
                                  "10. Pink"
                              };
            Console.WriteLine("Traditional foreach loop\n");
            //start the stopwatch for "for" loop
            var sw = Stopwatch.StartNew();
            foreach (string color in colors)
            {
                Console.WriteLine("{0}, Thread Id= {1}", color, Thread.CurrentThread.ManagedThreadId);
                Thread.Sleep(10);
            }
            Console.WriteLine("foreach loop execution time = {0} seconds\n", sw.Elapsed.TotalSeconds);
            Console.WriteLine("Using Parallel.ForEach");
            //start the stopwatch for "Parallel.ForEach"
             sw = Stopwatch.StartNew();
            Parallel.ForEach(colors, color =>
            {
                Console.WriteLine("{0}, Thread Id= {1}", color, Thread.CurrentThread.ManagedThreadId);
                Thread.Sleep(10);
            }
            );
            Console.WriteLine("Parallel.ForEach() execution time = {0} seconds", sw.Elapsed.TotalSeconds);
            Console.Read();
        }
    }
}

输出量

Traditional foreach loop
1. Red, Thread Id= 10
2. Green, Thread Id= 10
3. Blue, Thread Id= 10
4. Yellow, Thread Id= 10
5. White, Thread Id= 10
6. Black, Thread Id= 10
7. Violet, Thread Id= 10
8. Brown, Thread Id= 10
9. Orange, Thread Id= 10
10. Pink, Thread Id= 10
foreach loop execution time = 0.1054376 seconds

使用Parallel.ForEach示例

1. Red, Thread Id= 10
3. Blue, Thread Id= 11
4. Yellow, Thread Id= 11
2. Green, Thread Id= 10
5. White, Thread Id= 12
7. Violet, Thread Id= 14
9. Orange, Thread Id= 13
6. Black, Thread Id= 11
8. Brown, Thread Id= 10
10. Pink, Thread Id= 12
Parallel.ForEach() execution time = 0.055976 seconds

62
我真的不同意您的说法,即Parallel.ForEach(总是)更快。这实际上取决于循环内部操作的繁重程度。这可能不值得引入并行通信的开销。
Martao 2014年

1
好吧,每个线程的并行意味着要设置单独的线程来执行循环主体中的代码。即使.NET确实具有执行此操作的有效机制,这也是相当大的开销。因此,如果您只需要简单的操作(例如,求和或乘法),则并行foreach不应更快。
Martao

3
@Jignesh这甚至不是一个很好的度量示例,因此我完全不会引用它。删除“ Thread.Sleep(10);” 从每个循环主体中重试。

1
@Martao是正确的,问题在于对象锁定开销,其中并行方法可能比顺序方法更长。

8
@通常,我认为睡眠正是它成为一个很好的例子的原因。您不会在快速单次迭代中使用PFE(如Martao解释的那样)-因此,此答案使迭代速度变慢,并且PFE的(正确)优势得到了强调。我同意,尽管需要在答案中对此进行解释,但大胆的“总是更快”是非常误导的。
mafu

43
string[] lines = File.ReadAllLines(txtProxyListPath.Text);

// No need for the list
// List<string> list_lines = new List<string>(lines); 

Parallel.ForEach(lines, line =>
{
    //My Stuff
});

这将导致在循环内并行分析行。如果您想对Parallel类进行更详细的介绍,而不是“面向参考”的介绍,那么我就在TPL上写了一系列文章,其中包括Parallel.ForEach


9

对于大文件,请使用以下代码(您的内存不足)

Parallel.ForEach(File.ReadLines(txtProxyListPath.Text), line => {
    //Your stuff
});

2

这些行对我有用。

string[] lines = File.ReadAllLines(txtProxyListPath.Text);
var options = new ParallelOptions { MaxDegreeOfParallelism = Environment.ProcessorCount * 10 };
Parallel.ForEach(lines , options, (item) =>
{
 //My Stuff
});
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.