Phase 21 of 30 · Topic 21.2

`Parallel.ForEachAsync` (.NET 6+) Controlled Concurrency

1Concept

`Parallel.ForEachAsync` executes asynchronous operations over collections with bounded concurrency, avoiding ThreadPool overload compared to `Task.WhenAll` on huge datasets.

2Architecture Diagram

[ 10,000 URLs to Scrape ]
             │
   Parallel.ForEachAsync(MaxDegreeOfParallelism: 8)
             │
[ 8 Active Worker Pipelines (Locks bounded memory and network socket usage!) ]

3Code Example

C# 13 & .NET 9
using System;
using System.Collections.Generic;
using System.Threading.Tasks;

public class ParallelForEachAsyncDemo
{
    public static async Task Main()
    {
        List<int> jobIds = [1, 2, 3, 4, 5, 6, 7, 8];

        var parallelOptions = new ParallelOptions
        {
            MaxDegreeOfParallelism = 4 // Max 4 concurrent operations
        };

        await Parallel.ForEachAsync(jobIds, parallelOptions, async (jobId, ct) =>
        {
            await Task.Delay(15, ct);
            Console.WriteLine($"Completed Job #{jobId} on Thread {Environment.CurrentManagedThreadId}");
        });

        Console.WriteLine("All batch jobs completed successfully.");
    }
}

4Expected Output

Completed Job #1 on Thread 4
Completed Job #2 on Thread 6
Completed Job #3 on Thread 7
Completed Job #4 on Thread 8
Completed Job #5 on Thread 4
Completed Job #6 on Thread 6
Completed Job #7 on Thread 7
Completed Job #8 on Thread 8
All batch jobs completed successfully.

5Key Takeaways

  • `Parallel.ForEachAsync` replaces custom semaphore throttling loops.
  • Always pass `CancellationToken` to enable graceful shutdown.
  • Bounds memory usage when processing millions of stream items.