Concurrency Fundamentals
The distinction the whole domain rests on: overlapping progress versus simultaneous execution, why each exists, how to classify work as waiting-bound or compute-bound, and the honest admission that concurrency is always bought with complexity.
Three photos need thumbnails. They can run one after another, they can take turns on one core, or they can run at the same instant on three cores. Those are three different things, and almost every concurrency argument is really an argument about which one someone meant.
Q · Given three independent pieces of work, what are my actual options for running them, and how do they differ?
A system can be concurrent and not parallel, parallel and not concurrent, both, or neither. Operating Systems defines the two words; this lesson is about picking. Get it wrong and you will add threads to a waiting problem, or an event loop to a computing problem, and both feel like doing something.
Q · Does this workload need overlapping progress, simultaneous execution, both, or neither?
A request blocked for 500 ms on a database leaves a core doing nothing 500 million times over. Concurrency is the mechanism for giving that core to somebody else. Everything else in the model — event loops, coroutines, async I/O — is an implementation of that one idea.
Q · What is a thread actually doing while it waits 500 ms for a database, and what could it be doing instead?
One billion floats to sum. One core does it in 400 ms. Four cores should do it in 100 ms and will actually do it in about 130. Parallelism exists because a single core stopped getting faster around 2005 and the only remaining lever was more of them.
Q · When the work is pure computation and one core is already saturated, what is left?
Before you can choose an execution model you have to know what the work is doing with the wall clock. Compression, resizing, encryption, parsing and numerics compute. Network calls, database queries, disk reads and third-party APIs wait. Most real handlers do both, in phases, and the phases want different models.
Q · Is this unit of work spending its wall clock on a core or in a wait queue, and what does that dictate about how to run it?
Seven questions — bound, sharing, independence, ordering, task count, duration, isolation — and seven answers: sequential, threads, processes, async, worker pool, message passing, parallel algorithm. Every answer comes with why it wins, what it costs and how it fails, because a recommendation without those three is a preference.
Q · Given what I now know about this work, which execution model should carry it — and what am I signing up for?
A task is running, ready, waiting or blocked, and exactly one of those four uses a core. "Concurrent" means several tasks are in some state other than finished. Almost every confusing latency number resolves once you know which of the four a task was in and for how long.
Q · When we say several tasks are making progress at once, what is each of them actually doing at a given instant?
The counterweight to everything else in this domain. Coordination costs cycles, context switches cost cache, tasks cost memory, and the bugs cost you the one property you relied on — that a passing test means the code works. Concurrency is worth buying often. It is never free.
Q · What does concurrency cost, and how do I decide whether this workload can afford it?