Denys Alexieiev
Denys Alexieiev

Reputation: 254

C#. What happens if "after await" thread is busy?

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

Answers (3)

V0ldek
V0ldek

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 awaiting.

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

Theodor Zoulias
Theodor Zoulias

Reputation: 43429

It depends on the captured SynchronizationContext.

  1. 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.

  2. 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

GR7
GR7

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

Related Questions