Parallel.For, Thread Safety, Locks & Concurrent Collections Masterclass
Welcome to Phase 10 (Chapter 29): C# Parallel Programming, Parallel.For, Thread Safety & Concurrent Collections Masterclass! Parallel programming exploits multi-core CPU processors by distributing independent work across multiple threads simultaneously. In this chapter, we master Parallel.For, Parallel.ForEach, Task Parallel Library (TPL), thread safety, race conditions, lock, Monitor, Interlocked, concurrent collections (ConcurrentBag, ConcurrentDictionary), and performance considerations.
| Concept | async/await | Parallel Programming |
|---|---|---|
| Core Purpose | Handles I/O-Bound waits (network, disk) without blocking thread | Maximizes CPU usage for CPU-Bound computation across cores |
| Thread Usage | Single logical thread, released during I/O wait | Multiple physical threads running simultaneously |
| Best For | HTTP requests, file reads, database queries | Data processing, image rendering, number crunching |
using System.Threading.Tasks;
// 1. Parallel.For โ Distributes loop iterations across CPU cores
Parallel.For(0, 5, i =>
{
Console.WriteLine($"Parallel task {i} on Thread {Thread.CurrentThread.ManagedThreadId}");
});
// 2. Parallel.ForEach โ Parallel iteration over a collection
string[] cities = { "Hyderabad", "Bangalore", "Mumbai", "Delhi", "Chennai" };
Parallel.ForEach(cities, city =>
{
Console.WriteLine($"Processing city: {city} (Thread {Thread.CurrentThread.ManagedThreadId})");
});
int counter = 0;
object lockObject = new object();
Parallel.For(0, 1000, _ =>
{
// lock ensures only ONE thread enters this block at a time
lock (lockObject)
{
counter++;
}
});
Console.WriteLine($"Final Counter: {counter}"); // Always 1000 (thread-safe!)
// Alternative: Interlocked.Increment (lock-free atomic increment)
int atomicCounter = 0;
Parallel.For(0, 1000, _ => Interlocked.Increment(ref atomicCounter));
Console.WriteLine($"Interlocked Counter: {atomicCounter}");
Q1: What is a Race Condition?
A race condition occurs when two or more threads access and modify a shared variable simultaneously without synchronization, producing unpredictable and incorrect results (e.g., counter never reaching 1000 without lock).
Q2: When should I use ConcurrentDictionary vs Dictionary?
Use ConcurrentDictionary<K,V> from System.Collections.Concurrent when multiple threads need to read and write dictionary entries simultaneously. Standard Dictionary is NOT thread-safe.