That depends on the SynchronizationContext
of the thread on which the continuation was scheduled.
For example, when you're using async/await
in an app with a UI thread, like an ASP.NET or WPF app, any continuations scheduled on the UI thread will also execute on the UI thread. In a console app no SynchronizationContext
is captured, and the default behaviour will be to execute on whatever thread is available when the execution has to resume. If you give it some thought, it's actually much easier to execute on a "whatever" thread than on the exact same that scheduled the continuation.
All of that is only partially true, because you can configure an await
call to not capture the current SynchronizationContext
by calling ConfigureAwait(false)
on the returned Task
before await
ing.
To illustrate that, note that your code can deadlock if in an ASP.NET app you start asynchronous work on the UI thread and then force it to block until that work is completed, for example by calling Task.Result
on the returned Task
. Now you have a continuation that has to execute on the UI thread, but the UI thread is waiting for that continuation to execute, so none will ever proceed. If you do the same in a console app and there are free threads in the threadpool, the code won't block, because it's free to execute on a "whatever" thread. The same will happen in any app after calling ConfigureAwait(false)
- cause no SynchronizationContext
will be captured.
TL;DR: You've actually asked a rather simple question that has a horribly complicated answer. To state it shortly: the execution is allowed to continue on any thread, unless the SynchronizationContext
forces it to do otherwise. Going into more detail would turn this answer into a rather large blog post, and people much smarter than me have already produced blog posts about this, so I'll just link you to more sources on the topic:
Stephen Toub's FAQ about ConfigureAwait
Stephen Cleary's detailed MSDN article
Stephen Toub's "Await, SynchronizationContext, and Console Apps"
Stephen Cleary about ASP.NET Core SynchronizationContext
Stephen Cleary's "Don't Block on Async Code"
What does SynchronizationContext do?
Why the default SynchronizationContext is not captured in a Console App?