방법: 간단한 Parallel.ForEach 루프 작성How to: Write a simple Parallel.ForEach loop

이 예제는 Parallel.ForEach 루프를 사용하여 System.Collections.IEnumerable 또는 System.Collections.Generic.IEnumerable<T> 데이터 소스에 대해 데이터 병렬 처리를 사용하는 방법을 보여줍니다.This example shows how to use a Parallel.ForEach loop to enable data parallelism over any System.Collections.IEnumerable or System.Collections.Generic.IEnumerable<T> data source.

참고

이 문서에서는 람다 식을 사용하여 PLINQ에 대리자를 정의합니다.This documentation uses lambda expressions to define delegates in PLINQ. C# 또는 Visual Basic의 람다 식을 잘 모르는 경우 PLINQ 및 TPL의 람다 식을 참조하세요.If you are not familiar with lambda expressions in C# or Visual Basic, see Lambda expressions in PLINQ and TPL.

예제Example

이 예제에서는 C:\Users\Public\Pictures\Sample Pictures 폴더에 여러 .jpg 파일이 있다고 가정하고 Modified라는 새 하위 폴더를 만듭니다.This example assumes you have several .jpg files in a C:\Users\Public\Pictures\Sample Pictures folder and creates a new sub-folder named Modified. 예제를 실행할 때 Sample Pictures의 각 .jpg 이미지를 회전시키고 Modified에 저장합니다.When you run the example, it rotates each .jpg image in Sample Pictures and saves it to Modified. 필요에 따라 두 경로를 수정할 수 있습니다.You can modify the two paths as necessary.

using System;
using System.IO;
using System.Threading;
using System.Threading.Tasks;
using System.Drawing;

public class Example
{
    public static void Main()
    {
        // A simple source for demonstration purposes. Modify this path as necessary.
        string[] files = Directory.GetFiles(@"C:\Users\Public\Pictures\Sample Pictures", "*.jpg");
        string newDir = @"C:\Users\Public\Pictures\Sample Pictures\Modified";
        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 = Path.GetFileName(currentFile);
                                    var bitmap = new Bitmap(currentFile);

                                    bitmap.RotateFlip(RotateFlipType.Rotate180FlipNone);
                                    bitmap.Save(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 {filename} on thread {Thread.CurrentThread.ManagedThreadId}");
                                    //close lambda expression and method invocation
                                });

        // Keep the console window open in debug mode.
        Console.WriteLine("Processing complete. Press any key to exit.");
        Console.ReadKey();
    }
}
Imports System.IO
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() = Directory.GetFiles("C:\Users\Public\Pictures\Sample Pictures", "*.jpg")
        Dim newDir As String = "C:\Users\Public\Pictures\Sample Pictures\Modified"
        Directory.CreateDirectory(newDir)
       
        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 = Path.GetFileName(currentFile)
                                    Dim bitmap As New Bitmap(currentFile)

                                    bitmap.RotateFlip(System.Drawing.RotateFlipType.Rotate180FlipNone)
                                    bitmap.Save(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 {filename} on thread {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

Parallel.ForEach 루프는 Parallel.For 루프처럼 작동합니다.A Parallel.ForEach loop works like a Parallel.For loop. 해당 루프는 원본 컬렉션을 분할하고 시스템 환경에 따라 여러 스레드에서 작업을 예약합니다.The loop partitions the source collection and schedules the work on multiple threads based on the system environment. 시스템에 프로세서가 많을수록 병렬 메서드가 더 빠르게 실행됩니다.The more processors on the system, the faster the parallel method runs. 일부 원본 컬렉션의 경우 원본의 크기 및 수행되는 루프 작업의 종류에 따라 순차 루프가 더 빠를 수 있습니다.For some source collections, a sequential loop may be faster, depending on the size of the source and the kind of work the loop performs. 성능에 대한 자세한 내용은 데이터 및 작업 병렬 처리에서 발생할 수 있는 문제를 참조하세요.For more information about performance, see Potential pitfalls in data and task parallelism.

병렬 루프에 대한 자세한 내용은 방법: 간단한 Parallel.For 루프 작성을 참조하세요.For more information about parallel loops, see How to: Write a simple Parallel.For loop.

제네릭이 아닌 컬렉션에 Parallel.ForEach를 사용하려면 다음 예제와 같이 Enumerable.Cast 확장 메서드를 사용하여 컬렉션을 일반 컬렉션으로 변환할 수 있습니다.To use Parallel.ForEach with a non-generic collection, you can use the Enumerable.Cast extension method to convert the collection to a generic collection, as shown in the following example:

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

병렬 LINQ(PLINQ)를 사용하여 IEnumerable<T> 데이터 소스의 처리를 병렬 처리할 수도 있습니다.You can also use Parallel LINQ (PLINQ) to parallelize processing of IEnumerable<T> data sources. PLINQ를 통해 선언 쿼리 구문을 사용하여 루프 동작을 표현할 수 있습니다.PLINQ enables you to use declarative query syntax to express the loop behavior. 자세한 내용은 PLINQ(병렬 LINQ)를 참조하세요.For more information, see Parallel LINQ (PLINQ).

코드 컴파일 및 실행Compile and run the code

.NET Framework의 콘솔 애플리케이션 또는 .NET Core의 콘솔 애플리케이션으로 코드를 컴파일할 수 있습니다.You can compile the code as a console application for .NET Framework or as a console application for .NET Core.

Visual Studio에는 Windows Desktop 및 .NET Core용 Visual Basic 및 C# 콘솔 애플리케이션 템플릿이 있습니다.In Visual Studio, there are Visual Basic and C# console application templates for Windows Desktop and .NET Core.

명령줄에서 .NET Core CLI 명령(예: dotnet new console 또는 dotnet new console -lang vb)을 사용하거나 .NET Framework 애플리케이션에서 파일을 만들고 명령줄 컴파일러를 사용할 수 있습니다.From the command line, you can use either the .NET Core CLI commands (for example, dotnet new console or dotnet new console -lang vb), or you can create the file and use the command-line compiler for a .NET Framework application.

.NET Core 프로젝트에서 System.Drawing.Common NuGet 패키지를 참조해야 합니다.For a .NET Core project, you must reference the System.Drawing.Common NuGet package. Visual Studio에서 NuGet 패키지 관리자를 사용하여 패키지를 설치합니다.In Visual Studio, use the NuGet Package Manager to install the package. 혹은 *.csproj 또는 *.vbproj 파일에서 패키지에 대한 참조를 추가할 수 있습니다.Alternatively, you can add a reference to the package in your *.csproj or *.vbproj file:

<ItemGroup>
     <PackageReference Include="System.Drawing.Common" Version="4.5.1" />
</ItemGroup>

명령줄의 .NET Core 콘솔 애플리케이션을 실행하려면 애플리케이션이 포함된 폴더에서 dotnet run을 사용하세요.To run a .NET Core console application from the command line, use dotnet run from the folder that contains your application.

Visual Studio에서 콘솔 애플리케이션을 실행하려면 F5 키를 누르세요.To run your console application from Visual Studio, press F5.

참조See also