【问题标题】:How can I convert this foreach code to Parallel.ForEach?如何将此 foreach 代码转换为 Parallel.ForEach?
【发布时间】:2012-08-28 10:16:05
【问题描述】:

我对@9​​87654322@ 有点困惑。
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/…
  • @UjjwalManandhar 这实际上完全不同,因为它询问Parallel 类和使用 PLINQ 之间的区别。
  • 其他人已经回答了如何重写。那么它有什么作用呢?它对集合中的每个项目执行“操作”,就像普通的 foreach 一样。不同的是,并行版本可以同时做很多“动作”。在大多数情况下(取决于运行代码的计算机、它的繁忙程度以及其他因素)它会更快,这是最重要的优势。请注意,当您并行执行时,您无法知道处理项目的顺序。使用通常的(串行)foreach,您可以保证首先出现lines[0],然后是lines[1],依此类推。
  • @JeppeStigNielsen 它不会总是更快,因为并行处理会产生大量开销。这取决于您正在迭代的集合的大小和其中的操作。正确的做法是实际测量使用 Parallel.ForEach() 和使用 foreach() 之间的差异。很多时候,普通的 foreach() 更快。
  • @DaveBlack 当然。在每种情况下,都必须衡量它是更快还是更慢。我只是想概括地描述并行化。

标签: c# multithreading .net-4.0 parallel.foreach


【解决方案1】:

Foreach 循环:

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

Parallel.ForEach:

  • 以并行方式执行。
  • Parallel.ForEach 使用多个线程。
  • Parallel.ForEach 在 .Net 4.0 及更高版本的框架中定义。
  • 进程的执行可以更快,因为它们可以并行运行
    • 进程 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

【讨论】:

  • 我不太同意您的“声称”,即 Parallel.ForEach (总是)更快。这实际上取决于循环内部操作的繁重程度。这可能值得也可能不值得引入并行性的开销。
  • 好吧,每个线程的并行意味着设置单独的线程来执行循环体中的代码。尽管 .NET 确实具有执行此操作的有效机制,但这是相当大的开销。因此,如果您只需要一个简单的操作(例如求和或乘法),并行 foreach 应该不会更快。
  • @Jignesh 这甚至不是很好的测量示例,所以我根本不会提到这个。删除“Thread.Sleep(10);”从每个循环体中再试一次。
  • @Martao 是对的,问题在于对象锁定开销,其中并行方法可能比顺序方法更长。
  • @stenly 我认为睡眠正是它成为示例的原因。您不会使用具有快速单次迭代的 PFE(正如 Martao 解释的那样) - 所以这个答案使迭代变慢,并且突出了 PFE 的(正确)优势。我同意虽然这需要在答案中解释,但粗体的“总是更快”非常具有误导性。
【解决方案2】:
string[] lines = File.ReadAllLines(txtProxyListPath.Text);
List<string> list_lines = new List<string>(lines);
Parallel.ForEach(list_lines, line =>
{
    //Your stuff
});

【讨论】:

  • 只是想指出这一点(对于 OP 来说更多),以免误以为它只适用于 List&lt;T&gt; ;)
  • 感谢您的关注和回答。我在代码中使用了 List ,因为使用 HASH 列表删除了重复项。使用常规数组,我们无法轻松删除重复项:)。
  • 我很困惑这个答案被标记为正确答案,因为没有对原始帖子问题“什么是 Parallel.ForEach 以及它到底做了什么?”的解释......跨度>
  • @fosb 问题是问题标题已被编辑以完全改变含义......所以这个答案不再有意义。话虽如此,这仍然是一个糟糕的答案
【解决方案3】:
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 的系列文章,其中包括 section on Parallel.ForEach

【讨论】:

    【解决方案4】:

    对于大文件,请使用以下代码(您的内存消耗较少)

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

    【讨论】:

      【解决方案5】:

      这些行对我有用。

      string[] lines = File.ReadAllLines(txtProxyListPath.Text);
      var options = new ParallelOptions { MaxDegreeOfParallelism = Environment.ProcessorCount * 10 };
      Parallel.ForEach(lines , options, (item) =>
      {
       //My Stuff
      });
      

      【讨论】:

        【解决方案6】:

        我想补充一下并行选项。如果您没有提到它,默认情况下所有 RAM 都将用于此,这可能会给您带来生产问题。所以最好在代码中添加最大并行度。

        Parallel.ForEach(list_lines, new ParallelOptions { MaxDegreeOfParallelism = 2 }, line =>
        {
            
        });
        

        【讨论】:

          猜你喜欢
          • 2022-03-10
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 2010-10-09
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          相关资源
          最近更新 更多