Skip to Content
Course content

89: Parallel.For and Parallel.ForEach

Click on the "Edit" button in the top corner of the screen to edit your slide content.

When should I use Parallel.For versus Parallel.ForEach?

It's a simpler distinction than you might think. If you're dealing with a collection—like a List<T>, an array, or any IEnumerable—reach for Parallel.ForEach. It handles the iterator for you, and honestly, it's what I use 90% of the time because it's cleaner.

Parallel.For is for when you specifically need the index. If your logic depends on the current position (e.g., "if the index is even, do X; if odd, do Y") or if you're iterating through a range of numbers to perform a calculation, that's your tool.

// Use For when the index is the driver
Parallel.For(0, 1000, i => {
    ComputeComplexMath(i);
});

// Use ForEach when the data is the driver
var files = Directory.GetFiles(@"C:\Logs");
Parallel.ForEach(files, file => {
    ProcessLogFile(file);
});

Does this actually make my code faster every time?

Short answer: No. In fact, it can sometimes make your code slower. Parallelism isn't free; there is a significant overhead involved in partitioning the data, scheduling tasks on the ThreadPool, and managing thread synchronization.

I've seen developers try to use Parallel.ForEach on a list of ten strings to uppercase them, and it actually performed worse than a standard foreach loop. You want to use these when the work inside the loop is "heavy"—meaning it's CPU-intensive or takes a noticeable amount of time. If the loop body is just adding two numbers, stick to the sequential version. You're looking for a "grain size" that justifies the cost of spinning up those extra threads.

How do I stop my app from crashing when multiple threads touch the same variable?

This is where things get dangerous. If you have a shared variable—say, a total counter—and multiple threads try to increment it at the exact same millisecond, you'll run into a race condition. Your final count will be wrong, and you'll spend hours wondering why.

The easiest fix is the lock keyword, but that can create a bottleneck because threads have to wait in line. For simple numeric increments, I highly recommend the Interlocked class. It's way faster because it handles the synchronization at the CPU level.

int totalErrors = 0;
var logs = GetLogEntries();

Parallel.ForEach(logs, log => {
    if (log.Contains("ERROR")) {
        // DON'T DO: totalErrors++; 
        // DO THIS:
        Interlocked.Increment(ref totalErrors);
    }
});

Can I break out of a parallel loop early?

You can't use the break keyword here because the loop is running on multiple threads simultaneously; "breaking" one thread doesn't stop the others. Instead, you use the ParallelLoopState object, which is passed into the lambda expression.

Calling state.Stop() tells the system to try and stop all iterations as soon as possible. It's not an instantaneous "kill switch," but it's the correct way to signal that you've found what you were looking for and the rest of the work is unnecessary.

Parallel.ForEach(largeDataSet, (item, state) => {
    if (item.IsCorrupted) {
        Console.WriteLine("Found corruption! Stopping.");
        state.Stop(); 
    }
    PerformHeavyCheck(item);
});



📋 Practical Task

Exercise: Parallel Image Metadata Processor

You are building a tool that scans a large directory of simulated "Image" objects to calculate the total size of all images and identify any image that exceeds a certain size limit.

Requirements:

  • Create a class ImageFile with properties FileName (string) and FileSizeKB (int).
  • Generate a List<ImageFile> containing 10,000 random images.
  • Use Parallel.ForEach to iterate through the list.
  • Use Interlocked.Add to maintain a running total of the FileSizeKB across all threads.
  • Use a ConcurrentBag<string> to store the names of images that are larger than 5,000 KB (since List<T> is not thread-safe for adding items).
  • Print the final total size and the count of oversized images to the console.
Rating
0 0

There are no comments for now.

to be the first to leave a comment.