Concurrency vs Parallelism
Concurrency is dealing with multiple tasks at once (interleaving). Parallelism is doing multiple tasks at once (simultaneously).
The Tradeoff
Concurrency is dealing with multiple tasks at once (interleaving). Parallelism is doing multiple tasks at once (simultaneously). Concurrency is about structure; parallelism is about execution.
Concurrency: A Closer Look
Multiple tasks make progress by interleaving execution on one or few cores. Async I/O, event loops, coroutines.
The Good
- Works on single-core machines
- Lower resource usage
- Good for I/O-bound workloads
- Simpler memory model (no shared state issues)
The Bad
- Does not speed up CPU-bound work
- Requires async programming model
- Callback hell or complex async patterns
Parallelism: A Closer Look
Multiple tasks execute simultaneously on multiple CPU cores. Threads, processes, SIMD.
The Good
- Speeds up CPU-bound work linearly with cores
- True simultaneous execution
- Leverages multi-core hardware
The Bad
- Race conditions and deadlocks
- Shared memory complexity
- Diminishing returns (Amdahl's Law)
- Higher memory usage
Quick Comparison
| Concurrency | Parallelism | |
|---|---|---|
| Best for | Workload is I/O-bound (database queries, HTTP calls, file reads) | Workload is CPU-bound (data processing, ML training, video encoding) |
Real-World Examples
Node.js is concurrent but single-threaded — it handles thousands of connections by interleaving I/O operations on one core.
Apache Spark is parallel — it distributes data processing across hundreds of CPU cores on multiple machines.
Go combines both — goroutines provide concurrency (millions of lightweight threads) that the Go runtime schedules across all available CPU cores (parallelism).
Interview Advice
Know the difference: 'Concurrency is about dealing with lots of things at once. Parallelism is about doing lots of things at once.' — Rob Pike. Web servers need concurrency (I/O-bound). Data pipelines need parallelism (CPU-bound).
Source | System-Design-Overview
Practical Implementation for .NET Developers
In a .NET application, you would typically implement this pattern using the following approach:
ASP.NET Core setup: Create a service class that encapsulates the logic, register it with dependency injection, and inject it into your controllers or minimal API endpoints. The built-in DI container handles lifecycle management.
Entity Framework Core: For database interactions, EF Core provides the ORM layer. Use migrations for schema management and raw SQL for performance-critical queries. Consider Dapper for read-heavy paths where EF Core's overhead matters.
Azure integration: If deploying to Azure, leverage managed services — Azure Cache for Redis, Azure SQL, Azure Service Bus, Azure Cosmos DB. These eliminate operational overhead and provide built-in monitoring through Application Insights.
Testing: Use xUnit with Testcontainers for integration tests that spin up real databases in Docker. Mock external dependencies with NSubstitute. The WebApplicationFactory class lets you test your entire HTTP pipeline in-process.
Monitoring: Add Application Insights telemetry to track request latency, dependency calls, and custom metrics. Use structured logging with Serilog to make production debugging possible:
Log.Information("Processing order {OrderId} for {CustomerId}", orderId, customerId);
This gives you searchable, structured logs in Azure Monitor or Seq.
Real-World Decision Framework
Concurrency is about dealing with multiple tasks at once — managing their interleaving. Parallelism is about doing multiple tasks simultaneously on multiple cores. They are related but fundamentally different concepts.
Concurrency: Managing Multiple Tasks
Concurrency is a design pattern. A single-threaded Node.js server is concurrent but not parallel — it handles thousands of requests by switching between them using the event loop, but only one executes at a time.
Examples of concurrency:
- Web servers: Handle 10,000 simultaneous connections on a single thread using async I/O.
- GUI applications: Respond to user clicks while downloading a file in the background.
- Database connections: A connection pool manages 100 connections, allocating them to requests as needed.
- Operating systems: Run 200 processes on 8 cores by rapidly switching between them.
Parallelism: Doing Multiple Tasks Simultaneously
Parallelism is an execution strategy. It requires multiple CPUs or cores. A map-reduce job processing 1TB of data across 100 machines is parallel.
Examples of parallelism:
- Image processing: Apply a filter to each pixel simultaneously across 16 cores.
- Data pipelines: Process different data partitions on different machines simultaneously.
- Machine learning training: Train a model across 8 GPUs, each processing a different batch of data.
- Video encoding: Encode different frames of a video on different CPU cores simultaneously.
The Relationship
All parallel programs are concurrent (they manage multiple tasks), but not all concurrent programs are parallel (they might interleave on one core). A web server handling 10,000 connections is concurrent. A Spark job processing data across 100 nodes is both concurrent and parallel.
Why This Matters in System Design
In interviews, this distinction helps you make architecture decisions:
- I/O-bound workloads (API calls, database queries, file reads): Use concurrency with async/await. Adding more cores does not help if the bottleneck is waiting for network responses.
- CPU-bound workloads (encryption, compression, ML inference): Use parallelism. More cores directly increase throughput.
- Mixed workloads: Use both — async for I/O, parallel workers for CPU-intensive tasks.
.NET Implementation
Concurrency: Use async/await throughout ASP.NET Core — the runtime multiplexes thousands of requests across a thread pool. Parallelism: Use Parallel.ForEachAsync(), PLINQ, or Task.WhenAll() for CPU-bound work. Use System.Threading.Channels for producer-consumer patterns that combine both.