Reputation: 254
What happens in C# when awaitable task is finished but the thread in which async method had been started is unavailable (handles another request for example) ? Will then be used another thread instead of the first one, or execution will wait until the busy thread is available ?
Thanks in advance for your answers.
Upvotes: 3
Views: 717
Reputation: 10563
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?
Upvotes: 6
Reputation: 43429
It depends on the captured SynchronizationContext
.
In the simplest case there is no SynchronizationContext
(for example Console applications). In this case the continuation is not invoked in the original thread, but in a thread-pool thread. Normally a thread-pool thread will be available and will run the continuation immediately. Otherwise the continuation will run when a thread-pool thread becomes available.
In case a SynchronizationContext
exists (for example Windows Forms applications), the continuation will be invoked according to the rules of the specific SynchronizationContext
. For example the WindowsFormsSynchronizationContext
schedules the continuation through the windows message loop in the UI thread. If the UI thread happens to be idle, the continuation will run immediately. If the UI thread is busy, the continuation will be scheduled for a moment that the UI thread will be idle.
Update: Upon request by @GR7 I should add that it is possible to control whether the existing SynchronizationContext
is captured or not, by using the method ConfigureAwait
. By default the SynchronizationContext
is captured, so if you don't want to be captured you can configure the await
with ConfigureAwait(false)
. If a SynchronizationContext
does not exist (for example Console applications), the ConfigureAwait
has no effect.
Upvotes: 2
Reputation: 5186
You can control whether to wait for the original thread or not by using ConfigureAwait.
Unless you're await in the UI thread on a WinForms or WPF app, you should append .ConfigureAwait(false) to your await call so that the framework does not wait for the original thread and instead continues the work on the first available thread.
See a recent write up about ConfigureAwait by Stephen Toub -- ConfigureAwait FAQ
See also another SO question -- Best practice to use ConfigureAwait for all server side code
Upvotes: 1