关于IAwaiter等4组件协作及IAsyncMethodBuilder多awaiter关联的问询
await Scenarios) Great question—this is one of those parts of async/await that feels opaque at first, but clicks once you peek at what the compiler actually generates. Let’s break this down step by step, focusing specifically on your example of two sequential await calls:
var a = await MyMethod(); var b = await MyMethod();
First: Quick Roles of Each Component
Let’s start with a high-level overview to set the stage:
- IAwaitable: The "thing" you can
await(likeTask<T>). It exposes aGetAwaiter()method that returns an IAwaiter. - IAwaiter: Handles the low-level waiting logic—tracks if the operation is complete, fetches the final result, and triggers the continuation when done.
- IAsyncStateMachine: The compiler-generated struct/class that represents the "state" of your async method. It runs your code in chunks as each
awaitcompletes, keeping track of where it left off. - IAsyncMethodBuilder: The coordinator that ties everything together. It initializes the state machine, handles exception propagation, and manages the link between awaiters and the state machine.
Why Only One Awaiter in the State Machine?
Here’s the core insight: Your state machine only needs one awaiter field because it never waits on two operations at the same time (in your sequential scenario). Let’s walk through exactly what happens:
Initial Method Call: When you invoke your async method, the compiler creates an instance of the state machine and initializes the corresponding
IAsyncMethodBuilder(e.g.,AsyncTaskMethodBuilder<T>for methods returningTask<T>).First
awaitExecution:- The state machine’s
MoveNext()method runs for the first time. It callsMyMethod()to get anIAwaitable(aTask), then callsGetAwaiter()on it to grab anIAwaiter. - This awaiter is stored in the single awaiter field on the state machine.
- The state machine calls the builder’s
AwaitUnsafeOnCompleted()method, which links the awaiter to the state machine. This tells the awaiter: "When you’re done, trigger the state machine’sMoveNext()again." - The state machine pauses here until the first
MyMethod()completes.
- The state machine’s
First Await Completes:
- The awaiter signals it’s finished, which triggers
MoveNext()to run again. - The state machine calls the awaiter’s
GetResult()to get the value ofa, then that awaiter is no longer needed and gets overwritten soon after.
- The awaiter signals it’s finished, which triggers
Second
awaitExecution:- The state machine moves to its next state, calls the second
MyMethod()to get a newIAwaitable, and callsGetAwaiter()on it. - This new awaiter overwrites the same single awaiter field on the state machine.
- Again, it uses the builder to link this new awaiter to the state machine, pausing until the second operation finishes.
- The state machine moves to its next state, calls the second
Second Await Completes:
MoveNext()runs one last time, callsGetResult()to getb, then tells the builder to mark the entire async method as complete withSetResult().
The Builder’s Role Here
The IAsyncMethodBuilder doesn’t hold onto awaiters itself—it just provides the infrastructure to connect each awaiter to the state machine. Its job is to ensure that when an awaiter finishes, the state machine picks up where it left off. It doesn’t need to track multiple awaiters because the state machine only ever works with one at a time.
What If We Had Parallel Awaits?
If you used something like await Task.WhenAll(MyMethod(), MyMethod()), the state machine would still only use one awaiter—but that awaiter would be the one returned by Task.WhenAll(), which internally tracks both operations. The core idea remains: at any given time, the state machine is only waiting on one awaitable.
Recap of the Full Collaboration
To tie it all together:
- The compiler transforms your async method into a state machine that tracks execution progress.
- Each
awaitin your code translates to the state machine fetching an awaiter, storing it in its single awaiter field, and using the builder to link the awaiter to itself. - When an awaiter completes, it triggers the state machine to resume, process the result, and move to the next state (possibly fetching a new awaiter).
- The builder manages the lifecycle of the async operation (setting results, handling exceptions) and ensures the state machine is properly awakened.
内容的提问来源于stack exchange,提问作者Bercovici Adrian

