I believe this article by Stephen Toub can shed some light on this. In particular, this is a relevant passage about what happens during a context switch:
Whenever code awaits an awaitable whose awaiter says it’s not yet complete (i.e. the awaiter’s IsCompleted returns false), the method needs to suspend, and it’ll resume via a continuation off of the awaiter. This is one of those asynchronous points I referred to earlier, and thus, ExecutionContext needs to flow from the code issuing the await through to the continuation delegate’s execution. That’s handled automatically by the Framework. When the async method is about to suspend, the infrastructure captures an ExecutionContext. The delegate that gets passed to the awaiter has a reference to this ExecutionContext instance and will use it when resuming the method. This is what enables the important “ambient” information represented by ExecutionContext to flow across awaits.
Worth noting that the YieldAwaitable
returned by Task.Yield()
always returns false
.