Concurrency vs Parallelism
Scenario
Your server handles 1,000 requests per second. Does it have 1,000 CPUs, or is it juggling them really fast?
Mental model
Concurrency is about dealing with lots of things at once (juggling). Parallelism is about doing lots of things at once (having multiple jugglers).
Concurrency is a design structure. You break your program into independent tasks that can pause and resume. Parallelism is hardware execution. You literally run multiple tasks at the exact same nanosecond on different CPU cores.
Deep dive
Concurrency is about structure. When one task has to wait (like waiting for a database to respond), it pauses, and the CPU switches to another task. It's one chef making a salad while the steak is in the oven. The tasks make progress interleaved.
Concurrency = interleaved execution (one CPU core bouncing between tasks).
Parallelism is about hardware. Two tasks run at the exact same time on different CPU cores. It's two chefs in the kitchen: one making the salad, one cooking the steak, simultaneously.
Parallelism = simultaneous execution (multiple CPU cores at the same time).
Code examples
Concurrency (Interleaved - One Core)
import asyncio
async def fetch_data():
print("Start fetching")
await asyncio.sleep(1) # Simulates network I/O, yields control
print("Done fetching")
async def main():
# Both start, but they take turns on the single event loop thread
await asyncio.gather(fetch_data(), fetch_data())
asyncio.run(main())
These two tasks run concurrently. While the first task waits for `asyncio.sleep(1)`, it yields control, allowing the second task to start. They don't run at the exact same nanosecond, but they overlap in time.
Parallelism (Simultaneous - Multiple Cores)
from multiprocessing import Process
import time
def crunch_numbers():
print("Crunching")
time.sleep(1) # Simulates CPU work
print("Done crunching")
if __name__ == '__main__':
# Spawns separate OS processes, each can run on a different CPU core
p1 = Process(target=crunch_numbers)
p2 = Process(target=crunch_numbers)
p1.start(); p2.start()
p1.join(); p2.join()
These processes can run parallelly. The OS can schedule `p1` on Core 0 and `p2` on Core 1, meaning they physically execute instructions at the exact same time.
Common mistakes
- Thinking concurrency speeds up CPU-heavy work: If a task requires 10 seconds of pure math calculation on the CPU, concurrency won't make it faster. The single CPU will just bounce between the math tasks, taking the same total time (or slightly longer due to context switching). You need parallelism (multiple cores) to speed up CPU-bound work.
Recall questions
- What is the core difference between concurrency and parallelism?
- Does concurrency require multiple CPU cores?
- Which approach is better for I/O-bound tasks (like waiting for network requests)?
Questions & answers
If you have a Python web scraper downloading thousands of images, should you use concurrency or parallelism?
Concurrency. Downloading images is I/O-bound (most of the time is spent waiting for the network). A single core can concurrently manage thousands of active network connections using `asyncio` or threads.
Approach: Identify the bottleneck (I/O). State that concurrency is the right tool to overlap waiting times without the overhead of multiple processes.
Is Node.js concurrent or parallel?
Concurrent. Node.js runs on a single thread (event loop) and uses concurrency to handle many requests at once. It achieves high throughput by never blocking on I/O, but it does not execute parallelly on multiple cores by default.
Approach: Mention the single-threaded event loop and how it interleaves tasks.
Continue learning
Next: The GIL (Global Interpreter Lock)