如何:编写简单的 Parallel.ForEach 循环

本示例显示如何使用 Parallel.ForEach 循环对任何 System.Collections.IEnumerableSystem.Collections.Generic.IEnumerable<T> 数据源启用数据并行。

注意注意

本文档使用 lambda 表达式在 PLINQ 中定义委托。如果您不熟悉 C# 或 Visual Basic 中的 lambda 表达式,请参见 在 PLINQ 和 TPL 中的 Lambda 表达式

示例

' How to: Write a Simple Parallel.ForEach Loop
' IMPORTANT!!!: Add reference to System.Drawing.dll
Imports System.Threading
Imports System.Threading.Tasks
Imports System.Drawing

Module ForEachDemo

    Sub Main()
        ' A simple source for demonstration purposes. Modify this path as necessary.
        Dim files As String() = System.IO.Directory.GetFiles("C:\Users\Public\Pictures\Sample Pictures", "*.jpg")
        Dim newDir As String = "C:\Users\Public\Pictures\Sample Pictures\Modified"
        System.IO.Directory.CreateDirectory(newDir)

        ' Method signature: Parallel.ForEach(IEnumerable<TSource> source, Action<TSource> body)
        ' Be sure to add a reference to System.Drawing.dll.
        Parallel.ForEach(files, Sub(currentFile)
                                    ' The more computational work you do here, the greater 
                                    ' the speedup compared to a sequential foreach loop.
                                    Dim filename As String = System.IO.Path.GetFileName(currentFile)
                                    Dim bitmap As New System.Drawing.Bitmap(currentFile)

                                    bitmap.RotateFlip(System.Drawing.RotateFlipType.Rotate180FlipNone)
                                    bitmap.Save(System.IO.Path.Combine(newDir, filename))

                                    ' Peek behind the scenes to see how work is parallelized.
                                    ' But be aware: Thread contention for the Console slows down parallel loops!!!

                                    Console.WriteLine("Processing {0} on thread {1}", filename, Thread.CurrentThread.ManagedThreadId)
                                    'close lambda expression and method invocation
                                End Sub)


        ' Keep the console window open in debug mode.
        Console.WriteLine("Processing complete. Press any key to exit.")
        Console.ReadKey()
    End Sub
End Module
namespace ForEachDemo
{
    using System;
    using System.Drawing; // requires system.Drawing.dll
    using System.IO;
    using System.Threading;
    using System.Threading.Tasks;

    class SimpleForEach
    {
        static void Main()
        {
            // A simple source for demonstration purposes. Modify this path as necessary.
            string[] files = System.IO.Directory.GetFiles(@"C:\Users\Public\Pictures\Sample Pictures", "*.jpg");
            string newDir = @"C:\Users\Public\Pictures\Sample Pictures\Modified";
            System.IO.Directory.CreateDirectory(newDir);

            //  Method signature: Parallel.ForEach(IEnumerable<TSource> source, Action<TSource> body)
            Parallel.ForEach(files, currentFile =>
            {
                // The more computational work you do here, the greater 
                // the speedup compared to a sequential foreach loop.
                string filename = System.IO.Path.GetFileName(currentFile);
                System.Drawing.Bitmap bitmap = new System.Drawing.Bitmap(currentFile);

                bitmap.RotateFlip(System.Drawing.RotateFlipType.Rotate180FlipNone);
                bitmap.Save(System.IO.Path.Combine(newDir, filename));

                // Peek behind the scenes to see how work is parallelized.
                // But be aware: Thread contention for the Console slows down parallel loops!!!
                Console.WriteLine("Processing {0} on thread {1}", filename,
                                    Thread.CurrentThread.ManagedThreadId);

            } //close lambda expression
                 ); //close method invocation

            // Keep the console window open in debug mode.
            Console.WriteLine("Processing complete. Press any key to exit.");
            Console.ReadKey();
        }
    }
}

ForEach 循环的工作方式类似于 For 循环。 根据系统环境,对源集合进行分区,并在多个线程上计划工作。 系统中的处理器越多,并行方法的运行速度越快。 对于某些源集合,顺序循环可能更快,具体取决于源的大小和正在执行的工作类型。 有关性能的更多信息,请参见数据并行和任务并行中的潜在缺陷

有关并行循环的更多信息,请参见如何:编写简单的 Parallel.For 循环

若要将 ForEach 用于非泛型集合,可以使用 Cast<TResult> 扩展方法将集合转换为泛型集合,如下面的示例所示:

Parallel.ForEach(nonGenericCollection.Cast(Of Object), _
                 Sub(currentElement)
                     ' ... work with currentElement
                 End Sub)
Parallel.ForEach(nonGenericCollection.Cast<object>(),
    currentElement =>
    {
    });

还可以使用并行 LINQ (PLINQ) 来并行处理 IEnumerable<T> 数据源。 PLINQ 使您可以使用声明性的查询语法表达循环行为。 有关更多信息,请参见并行 LINQ (PLINQ)

编译代码

  • 将此代码复制并粘贴到 Visual Studio 2010 控制台应用程序项目中。

  • 添加对 System.Drawing.dll 的引用

  • 按 F5

请参见

概念

数据并行(任务并行库)

.NET Framework 中的并行编程

并行 LINQ (PLINQ)