How to limit the maximum number of parallel tasks in C
Interview Questions practice on Codemia
Over 8,000 real interview questions from top companies, searchable by company and role.
Introduction
In C#, the three main ways to limit parallel tasks are SemaphoreSlim for async/await patterns, Parallel.ForEach with MaxDegreeOfParallelism for CPU-bound work, and Channel<T> or ActionBlock<T> for producer-consumer patterns. SemaphoreSlim is the most flexible and works with async code. Parallel.ForEach is simpler but blocks the calling thread. Choose based on whether your workload is I/O-bound (use SemaphoreSlim) or CPU-bound (use Parallel.ForEach).
SemaphoreSlim (Async/Await)
The most common approach for limiting concurrent async operations:
SemaphoreSlim(5) allows at most 5 tasks to execute concurrently. When a task finishes and calls Release(), the next waiting task proceeds.
Parallel.ForEach with MaxDegreeOfParallelism
For CPU-bound synchronous work:
MaxDegreeOfParallelism limits the number of threads used by the Parallel.ForEach loop. Set it to Environment.ProcessorCount to match the number of CPU cores.
Parallel.ForEachAsync (.NET 6+)
Combines Parallel.ForEach with async support:
This is the cleanest approach in .NET 6+ — it handles semaphore-like limiting internally and supports CancellationToken.
ActionBlock from TPL Dataflow
ActionBlock<T> from the System.Threading.Tasks.Dataflow NuGet package provides built-in concurrency limiting:
Channel-Based Producer-Consumer
Exactly 5 consumer tasks process items from the channel concurrently.
Comparison
| Approach | Async Support | Best For | .NET Version |
SemaphoreSlim | Yes | I/O-bound async work | All |
Parallel.ForEach | No | CPU-bound sync work | All |
Parallel.ForEachAsync | Yes | I/O-bound (simplest API) | .NET 6+ |
ActionBlock<T> | Yes | Producer-consumer pipelines | NuGet |
Channel<T> + consumers | Yes | Custom backpressure | .NET Core 3.0+ |
With Cancellation Support
Common Pitfalls
- Forgetting to Release the semaphore: If an exception occurs before
semaphore.Release(), the semaphore count decreases permanently, eventually deadlocking the application. Always release in afinallyblock. - Using
Parallel.ForEachwith async lambdas:Parallel.ForEachdoes not await async lambdas — it treats them as fire-and-forget. UseParallel.ForEachAsync(.NET 6+) orSemaphoreSlimfor async work. - Setting
MaxDegreeOfParallelismtoo high for I/O: For I/O-bound tasks (HTTP calls, database queries), more parallelism does not always mean faster. It can overwhelm the target server or exhaust connection pools. Start with 5-10 and tune based on results. Task.WhenAllwith no semaphore:Task.WhenAll(items.Select(ProcessAsync))launches ALL tasks immediately with no concurrency limit. If you have 10,000 URLs, all 10,000 HTTP requests fire at once. Always use a semaphore orParallel.ForEachAsyncto limit concurrency.- Disposing
SemaphoreSlimwhile tasks are waiting: Disposing the semaphore while tasks are blocked onWaitAsync()throwsObjectDisposedException. Wait for all tasks to complete before disposing the semaphore.
Summary
- Use
SemaphoreSlimwithWaitAsync/Releasefor async concurrency limiting - Use
Parallel.ForEachwithMaxDegreeOfParallelismfor synchronous CPU-bound work - Use
Parallel.ForEachAsync(.NET 6+) for the simplest async concurrency limiting API - Always release semaphores in
finallyblocks to prevent deadlocks - Choose concurrency limits based on the workload type — CPU cores for CPU-bound, lower limits for I/O-bound
Related reading
- How to load an image asynchronously?
- How to load CSS Asynchronously
- How to load video url's thumbnail image on tableview list Asynchronously
- How to make a clean Asynchronous loop?
- How to Load an Assembly to AppDomain with all references recursively?
- How to locate fuslogvw.exe on my machine?
- How to make a delayed future cancelable in Dart?
- How to make a function wait until a callback has been called using node.js

OOD Fundamentals
Master object-oriented design from first principles, SOLID, design patterns, and classic interview problems with hands-on coding.
View the courseTrack what you have practised
A free account saves your progress, solutions and study plan across every problem on Codemia.
Interview Questions practice on Codemia
Over 8,000 real interview questions from top companies, searchable by company and role.