Concurrency, Parallelism, and Async
Problem
Your application needs a customer's profile and recent orders from two independent services. It waits for the profile before requesting the orders. You want to overlap the work without confusing that with using more CPU cores.
Solution — Concurrency, Parallelism, and Asynchronous Execution
Concurrency: tasks overlap
Concurrency means several tasks are in progress during overlapping periods. A single thread can support concurrency by switching between tasks or handling another task while one waits.
Parallelism: work runs at the same time
Parallelism means work actually executes at the same time, for example on different CPU cores. It can speed up independent calculations when the benefit exceeds the coordination overhead.
Async: pause the method while waiting
Asynchronous execution lets an operation return control before its result is ready. The caller can do other work and handle completion later. With asynchronous I/O, an application can start both requests and wait for both results without keeping a dedicated thread blocked for each request. The exact mechanism depends on the runtime and API.
What happens to the thread? A C#/.NET example
The async keyword marks a method that can use await. The possible pause happens at await, not at the async keyword. Consider a method that prepares a request, awaits an HTTP response, and then processes the result.
Step 1 — Run the initial code. When called, this method starts on the calling thread. That thread executes the preparation and starts the HTTP operation. Adding async does not automatically move this work onto another thread.
Step 2 — Reach await. If the HTTP operation is still unfinished, the method pauses. Its local state and the point where it should resume are preserved. It returns control to its caller, with a Task representing its eventual completion.
Step 3 — Let the thread do other work. The thread can continue executing the caller. Once control returns to the runtime or event loop, it can handle other work, such as another request or a user-interface event. With genuine asynchronous I/O, no dedicated thread needs to sit blocked waiting for this HTTP response.
This is what people mean when they say the thread is “freed”: the paused method stops occupying it while waiting. The thread is still alive, and the caller may still have work to execute on it.
Step 4 — Resume the method. When the operation completes, execution resumes at the await to obtain the result or observe an error. After a successful await, the remaining code runs on a thread again. Depending on the runtime's scheduling and the application's context, this may be the original thread or another thread.
Before await → run preparation on the calling thread Unfinished I/O at await → pause this method; return to caller After successful await → continue with the result
If the operation is already complete when C# reaches await, the method can continue immediately without pausing. CPU calculations before or after await still occupy the thread executing them.
Core idea: concurrency overlaps tasks, parallelism executes work simultaneously, and await can pause a method without blocking its thread while it waits.