Courses/C#/Parallel Programming

    Parallel Programming with PLINQ & Parallel.For

    Lesson 22 โ€ข Advanced Track

    What You'll Learn

    • Parallelise CPU-bound loops with Parallel.For and Parallel.ForEach
    • Control the degree of parallelism with ParallelOptions
    • Turn any LINQ query into a parallel query with .AsParallel() (PLINQ)
    • Preserve ordering in PLINQ with .AsOrdered()
    • Avoid race conditions with Interlocked, lock, and concurrent collections
    • Use ConcurrentBag, ConcurrentDictionary, and ConcurrentQueue for thread safety

    ๐Ÿ’ก Real-World Analogy

    Async is like a waiter serving multiple tables โ€” one person doing many things efficiently by not blocking. Parallel is like hiring more cooks โ€” multiple people doing different parts of the work simultaneously. Use async for I/O-bound work (waiting for databases, APIs). Use parallel for CPU-bound work (crunching numbers, processing images). They solve different problems.

    ๐Ÿ“Š Async vs Parallel

    FeatureAsync/AwaitParallel
    Best forI/O-bound (network, disk)CPU-bound (computation)
    Threads used1 (non-blocking)Multiple (thread pool)
    Scaling factorThousands of concurrent opsLimited by CPU cores
    Shared stateUsually not an issueRace conditions risk
    APIasync/await, TaskParallel.For, PLINQ

    1. Parallel.For & Parallel.ForEach

    Parallel.For distributes loop iterations across thread pool threads. Each iteration runs independently on a different core. Use MaxDegreeOfParallelism to limit how many threads run simultaneously โ€” useful for resource-constrained environments.

    Parallel.For & ForEach

    Compare sequential vs parallel loops and control parallelism.

    Try it Yourself ยป
    C#
    using System;
    using System.Diagnostics;
    using System.Threading;
    using System.Threading.Tasks;
    
    class Program
    {
        // Simulate CPU-intensive work
        static double HeavyComputation(int n)
        {
            double result = 0;
            for (int i = 0; i < 100_000; i++)
                result += Math.Sqrt(i * n);
            return result;
        }
    
        static void Main()
        {
            const int items = 100;
    
            // Sequential
            var sw = Stopwatch.StartNew();
            double[] seqResults = new double[item
    ...

    2. PLINQ โ€” Parallel LINQ

    Add .AsParallel() to any LINQ query to parallelise it. PLINQ partitions the data and processes chunks on different threads. Use .AsOrdered() if you need results in the original order (at a small performance cost).

    PLINQ

    Find primes with parallel LINQ and control ordering.

    Try it Yourself ยป
    C#
    using System;
    using System.Diagnostics;
    using System.Linq;
    
    class Program
    {
        static bool IsPrime(int n)
        {
            if (n < 2) return false;
            for (int i = 2; i <= Math.Sqrt(n); i++)
                if (n % i == 0) return false;
            return true;
        }
    
        static void Main()
        {
            int range = 1_000_000;
    
            // Sequential LINQ
            var sw = Stopwatch.StartNew();
            int seqCount = Enumerable.Range(2, range)
                .Where(IsPrime)
                .Count();
            s
    ...

    3. Thread Safety & Concurrent Collections

    When parallel code shares state, you get race conditions โ€” two threads modifying the same variable simultaneously. Use Interlocked for atomic operations, or thread-safe collections like ConcurrentBag and ConcurrentDictionary.

    Thread Safety

    Fix race conditions with Interlocked and concurrent collections.

    Try it Yourself ยป
    C#
    using System;
    using System.Collections.Concurrent;
    using System.Threading;
    using System.Threading.Tasks;
    
    class Program
    {
        static void Main()
        {
            // โŒ WRONG โ€” shared counter without synchronization
            int unsafeCount = 0;
            Parallel.For(0, 100_000, _ =>
            {
                unsafeCount++;  // Race condition!
            });
            Console.WriteLine($"Unsafe count (should be 100000): {unsafeCount}");
    
            // โœ… Option 1: Interlocked (atomic operations)
            int safeCou
    ...

    Pro Tips

    • ๐Ÿ’ก Don't parallelise everything: Parallel overhead (thread management, synchronisation) means small workloads are actually slower in parallel. Profile first.
    • ๐Ÿ’ก PLINQ's sweet spot: Data sets with 10,000+ items and CPU-intensive per-item work. For small collections, sequential LINQ is faster.
    • ๐Ÿ’ก Prefer Interlocked over lock: Interlocked.Increment is lock-free and much faster than wrapping in a lock statement.
    • ๐Ÿ’ก Use Partitioner for uneven work: Partitioner.Create(0, items, chunkSize) gives you control over how work is divided.

    Common Mistakes

    • Using Parallel for I/O: Parallel.ForEach with HTTP calls blocks thread pool threads. Use Task.WhenAll with async instead.
    • Shared mutable state: count++ in a Parallel.For body is a race condition. Use Interlocked.Increment or local aggregation.
    • PLINQ without AsOrdered: By default, PLINQ returns results in arbitrary order. If order matters, add .AsOrdered().
    • Too many threads: Setting MaxDegreeOfParallelism = 100 on an 8-core machine causes excessive context switching. Match to CPU cores.
    • Exceptions in parallel: Parallel methods wrap exceptions in AggregateException. Catch and inspect .InnerExceptions.

    ๐ŸŽ‰ Lesson Complete

    • โœ… Parallel.For and Parallel.ForEach parallelise CPU-bound loops
    • โœ… MaxDegreeOfParallelism controls how many threads run simultaneously
    • โœ… PLINQ: .AsParallel() turns any LINQ query into a parallel query
    • โœ… .AsOrdered() preserves element order in PLINQ results
    • โœ… Interlocked provides lock-free atomic operations for shared counters
    • โœ… ConcurrentBag, ConcurrentDictionary for thread-safe collections
    • โœ… Next lesson: Memory Management & Garbage Collector Internals

    Sign up for free to track which lessons you've completed and get learning reminders.

    Previous

    Cookie & Privacy Settings

    We use cookies to improve your experience, analyze traffic, and show personalized ads. You can manage your preferences below.

    By clicking "Accept All", you consent to our use of cookies for analytics and personalized advertising. You can customize your preferences or reject non-essential cookies.

    Privacy Policy โ€ข Terms of Service