1. TASK — WHAT IT ACTUALLY IS¶
Definition¶
-
Taskrepresents an operation that will complete in the future -
It is NOT:
-
a thread
-
async by itself
-
Types¶
Task // no result
Task<int> // returns value
Internal Responsibilities¶
Task contains:
- completion state (Pending, Completed, Faulted)
- result (if Task<T>)
- exception (if any)
- list of continuations (callbacks)
Example — CPU Task¶
var task = Task.Run(() => 10);
ThreadPool thread executes work
Task completes when work finishes
Example — IO Task¶
var task = Task.Delay(1000);
No thread doing work
Timer → completes Task later
Interview Answer¶
A Task represents an asynchronous operation that may complete in the future. It encapsulates the state, result, and continuations of the operation, and may or may not involve a thread depending on whether the work is CPU-bound or I/O-bound.
2. async / await — WHAT THEY DO¶
Definition¶
-
async→ enables use ofawait -
await→ pauses method until Task completes
Key Behavior¶
await does:
1. Check if Task is completed
2. If yes → continue synchronously
3. If no → register continuation + return
Internal Form¶
var awaiter = task.GetAwaiter();
if (!awaiter.IsCompleted)
{
awaiter.OnCompleted(MoveNext);
return;
}
awaiter.GetResult();
Example¶
async Task<int> Get()
{
await Task.Delay(1000);
return 10;
}
Interview Answer¶
Async/await enables non-blocking execution by splitting methods into state machines. When an await is encountered, execution is paused, and a continuation is registered to resume once the Task completes.
3. STATE MACHINE¶
Definition¶
-
Compiler converts async method into state machine
-
Stores:
-
current execution point
-
local variables
-
awaiter
-
Example Transformation¶
async Task<int> Get()
{
await Task.Delay(1000);
return 10;
}
Becomes conceptually¶
state = 0:
start delay
if not complete:
save state = 1
register continuation
return
state = 1:
resume
return 10
Storage¶
State machine lives on heap
Task returned immediately
Interview Answer¶
Async methods are compiled into state machines that track execution state and resume execution after awaited operations complete.
4. STACK vs STATE MACHINE¶
Before await¶
Stack:
Level1
↓
Level2
↓
Level3
At await¶
State saved
Stack unwinds
After await¶
Stack: empty
Heap:
Level1StateMachine
Level2StateMachine
Level3StateMachine
Resume¶
ThreadPool thread:
Level3.MoveNext()
→ Level2.MoveNext()
→ Level1.MoveNext()
Interview Answer¶
When an await is encountered, the call stack unwinds and execution state is stored in a heap-allocated state machine. Execution resumes later via continuations instead of stack frames.
5. YOUR CONFUSION POINT — “WHY LEVEL1 STOPS”¶
Code¶
async Task Level1()
{
await Level2();
}
async Task Level2()
{
await Level3();
}
async Task Level3()
{
await Task.Delay(1000);
}
Execution¶
Step 1¶
Level1 calls Level2
Level2 calls Level3
Level3 calls Task.Delay
Step 2 — Level3 await¶
Task.Delay not completed
→ Level3 registers continuation
→ returns Task
Step 3 — Level2 await¶
Level3 Task not completed
→ Level2 registers continuation
→ returns Task
Step 4 — Level1 await¶
Level2 Task not completed
→ Level1 registers continuation
→ returns Task
Key Insight¶
await causes pause — not Task itself
Interview Answer¶
Each level awaits the Task returned by the next level. If the Task is incomplete, the method registers its continuation and returns, causing the call stack to unwind.
6. WHAT IS “SPECIAL” ABOUT Task.Delay¶
Definition¶
-
Returns a Task that is not completed initially
-
Completes later via timer
Behavior¶
Task.Delay:
creates Task (IsCompleted = false)
registers timer
timer completes Task later
Comparison¶
await Task.FromResult(10);
IsCompleted = true → no pause
await Task.Delay(1000);
IsCompleted = false → pause
Key Insight¶
Async boundary = first incomplete Task
Interview Answer¶
Task.Delay represents a real asynchronous boundary because it returns an incomplete Task that is completed later by a timer, causing the awaiting method to pause.
7. CONTINUATIONS (CORE MECHANISM)¶
Definition¶
- Continuation = method to run after Task completes
Registration¶
await Level3();
Level3.Task:
continuations → Level2.MoveNext
Execution¶
Level3 completes →
run Level2.MoveNext →
Level2 completes →
run Level1.MoveNext
Chain¶
Level3.Task → Level2 continuation
Level2.Task → Level1 continuation
Key Insight¶
No state machine calls another directly
All communication via Task
Interview Answer¶
Await registers a continuation on the awaited Task. When the Task completes, it invokes the continuation, resuming execution of the awaiting method.
8. THREAD USAGE¶
CPU-bound¶
await Task.Run(() => Work());
Thread actively executes work
IO-bound¶
await Task.Delay(1000);
No thread during wait
Timer completes Task
Resume¶
Continuation runs on ThreadPool thread
Interview Answer¶
Async does not guarantee new threads. CPU-bound tasks use ThreadPool threads, while I/O-bound tasks do not consume threads while waiting and resume via continuations.
9. SYNCHRONIZATION CONTEXT DEADLOCK¶
Code¶
GetDataAsync().Result;
Flow¶
Thread blocked
Continuation needs same thread
→ deadlock
Cause¶
await captures context
.Result blocks thread
Fix¶
await GetDataAsync();
Interview Answer¶
Deadlocks occur when a thread is blocked waiting for a Task, while the Task’s continuation requires that same thread due to SynchronizationContext capture.
10. FINAL MODEL¶
Task:
holds completion + continuations
await:
checks completion
registers continuation if needed
state machine:
stores execution state
thread:
executes only when needed
FINAL INTERVIEW ANSWER¶
Async/await in C# is implemented using Tasks and compiler-generated state machines. When an await is encountered, the method checks if the Task is complete; if not, it registers a continuation and returns, unwinding the stack. The Task completes later, triggering the continuation and resuming execution. This model allows non-blocking execution and efficient use of threads.