C# Foreach와 Parallel.ForEach의 핵심 차이점
C#의 foreach 루프는 단일 스레드에서 실행되며, 컬렉션의 각 항목을 순차적으로 하나씩 처리합니다. 반면 Parallel.ForEach 루프는 여러 스레드를 활용하여 작업을 병렬 방식으로 수행합니다. 즉, 이전 항목의 처리가 완료되기를 기다리지 않고 모든 항목을 동시에 순회할 수 있습니다.
Parallel.ForEach는 일반적인 foreach보다 실행 속도가 빠르다는 장점이 있습니다. Parallel.ForEach 루프를 사용하려면 System.Threading.Tasks 네임스페이스를 프로젝트에 추가해야 합니다.
예제 코드
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Threading;
using System.Threading.Tasks;
namespace DemoApplication{
class Demo{
static void Main(string[] args){
var animals = new List<string>{
"cat", "rat", "deer", "elephant", "lion", "tiger", "dog", "pig", "buffalo", "rabbit", "horse", "cheetah", "cow", "goat", "sheep", "donkey", "zebra", "wolf", "fox", "lepoard", "monkey", "kangaroo", "girafee", "hippo"
};
var stopWatch = Stopwatch.StartNew();
foreach (string animal in animals){
Console.WriteLine($"Fruit Name: {animal}, Thread Id={Thread.CurrentThread.ManagedThreadId}");
}
Console.WriteLine($"foreach loop execution time = {stopWatch.Elapsed.TotalSeconds} seconds");
var stopWatch2 = Stopwatch.StartNew();
Parallel.ForEach(animals, animal =>{
Console.WriteLine($"Fruit Name: {animal}, Thread Id={Thread.CurrentThread.ManagedThreadId}");
});
Console.WriteLine($"Parallel foreach loop execution time = {stopWatch2.Elapsed.TotalSeconds} seconds");
Console.ReadLine();
}
}
}실행 결과
위 코드를 실행하면 다음과 같은 결과가 출력됩니다.
Fruit Name: cat, Thread Id= 1 Fruit Name: rat, Thread Id= 1 Fruit Name: deer, Thread Id= 1 Fruit Name: elephant, Thread Id= 1 Fruit Name: lion, Thread Id= 1 Fruit Name: tiger, Thread Id= 1 Fruit Name: dog, Thread Id= 1 Fruit Name: pig, Thread Id= 1 Fruit Name: buffalo, Thread Id= 1 Fruit Name: rabbit, Thread Id= 1 Fruit Name: horse, Thread Id= 1 Fruit Name: cheetah, Thread Id= 1 Fruit Name: cow, Thread Id= 1 Fruit Name: goat, Thread Id= 1 Fruit Name: sheep, Thread Id= 1 Fruit Name: donkey, Thread Id= 1 Fruit Name: zebra, Thread Id= 1 Fruit Name: wolf, Thread Id= 1 Fruit Name: fox, Thread Id= 1 Fruit Name: lepoard, Thread Id= 1 Fruit Name: monkey, Thread Id= 1 Fruit Name: kangaroo, Thread Id= 1 Fruit Name: girafee, Thread Id= 1 Fruit Name: hippo, Thread Id= 1 foreach loop execution time = 0.0129221 seconds Fruit Name: cat, Thread Id= 1 Fruit Name: rat, Thread Id= 1 Fruit Name: deer, Thread Id= 1 Fruit Name: elephant, Thread Id= 1 Fruit Name: lion, Thread Id= 1 Fruit Name: tiger, Thread Id= 1 Fruit Name: pig, Thread Id= 1 Fruit Name: buffalo, Thread Id= 1 Fruit Name: dog, Thread Id= 3 Fruit Name: sheep, Thread Id= 3 Fruit Name: donkey, Thread Id= 3 Fruit Name: rabbit, Thread Id= 1 Fruit Name: goat, Thread Id= 5 Fruit Name: fox, Thread Id= 5 Fruit Name: lepoard, Thread Id= 5 Fruit Name: cow, Thread Id= 4 Fruit Name: hippo, Thread Id= 4 Fruit Name: kangaroo, Thread Id= 9 Fruit Name: girafee, Thread Id= 6 Fruit Name: monkey, Thread Id= 5 Fruit Name: horse, Thread Id= 1 Fruit Name: cheetah, Thread Id= 1 Fruit Name: zebra, Thread Id= 3 Fruit Name: wolf, Thread Id= 3 Parallel foreach loop execution time = 0.0092988 seconds
결과 분석 및 주의사항
위 실행 결과를 보면 두 가지 중요한 차이점을 확인할 수 있습니다. 첫째, 일반 foreach는 모든 항목이 동일한 스레드(ID 1)에서 순서대로 처리되는 반면, Parallel.ForEach는 여러 개의 서로 다른 스레드(ID 1, 3, 4, 5, 6, 9 등)에 걸쳐 작업이 분산되어 실행됩니다. 둘째, Parallel.ForEach의 실행 시간(0.0092988초)이 일반 foreach(0.0129221초)보다 짧아 더 나은 성능을 보여줍니다.
다만 Parallel.ForEach 사용 시 몇 가지 유의할 점이 있습니다. 항목의 처리 순서가 보장되지 않으므로 순서가 중요한 로직에는 적합하지 않으며, 여러 스레드가 공유 자원에 동시에 접근할 수 있으므로 스레드 안전성(thread safety)을 반드시 고려해야 합니다. 따라서 CPU 집약적이고 각 항목이 독립적으로 처리될 수 있는 대량의 작업에 Parallel.ForEach를 활용하는 것이 가장 효과적입니다.