4

According to this article ASP.NET requires using the same SynchronizationContext for asynchronous operations in the controller, otherwise it blocks the running thread. As a conclusion author mentioned that we should use both the methods as the best practice to prevent threads deadlocks:

  1. In your “library” async methods, use ConfigureAwait(false) wherever possible.
  2. Don’t block on Tasks; use async all the way down.

    Note: It is best to apply both best practices. Either one will prevent the deadlock, but both must be applied to achieve maximum performance and responsiveness.

But what purpose led Microsoft to use the same SynchronizationContext? May be using of the ConfigureAwait(false), which disables the same sync context restriction, might lead to e.g. unpredictable behavior or performance issues. Thus, is it really good practice to use the ConfigureAwait(false) wherever is possible?

2 Answers 2

3

ASP.NET requires using the same SynchronizationContext for asynchronous operations in the controller, otherwise it blocks the running thread.

I'm not really sure what that statement means. ASP.NET doesn't require "the same" synchronization context. In order for you to be inside your HttpContext, you need that SynchronizationContext.

But what purpose led Microsoft to use the same SynchronizationContext?

I suggest you read It's All About SynchronizationContext to get the bigger picture about sync contexts. Basically, it's a mechanism which allows you to post continuations on a particular thread. It can be used, for example, to marshal work back onto the UI message loop thread inside UI applications.

May be using of the ConfigureAwait(false), which disables the same sync context restriction, might lead to e.g. unpredictable behavior or performance issues

On the contrary. Marshaling work back onto the sync context does have (very minimal) overhead, as the request needs to be posted (using the abstract SynchronizationContext.Post) back onto the desired thread and context. By using ConfigureAwait(false), you're saving that time and simply continuing execution on which ever thread was allocated.

Thus, is it really good practice to use the ConfigureAwait(false) wherever is possible?

The primary reason to do that is to avoid deadlocks. When someone calling Task.Result or Task.Wait instead of asynchronously waiting using await, you get the classic deadlock scenario where the sync context is attempting to post the continuation back onto the thread, but it is currently blocked because Result and Wait are blocking calls. The other reason is the minor performance overhead you get.

Edit:

Why doesn't Microsoft encapsulate this method inside the Task class? It looks like the ConfigureAwait(false) has just only the pluses. Is there any minuses?

Let's image the following scenario: You execute a method which returns a Task and is awaited using await, and right after that you update some UI element. Now, what would be more natural to you? Would you prefer to implicitly return to the same "environment" you were before (the UI thread), or would you prefer that you had to explicitly specify that you want to return to that same environment?

I think that the former "feels" more natural to the user.

Example:

public Task FetchAndReturnAsync(string url) { var httpClient = new HttpClient(); return httpClient.GetAsync(url); } 

You call it like this:

var awesomeUiResult = await FetchAndReturnAsync("http://www.google.com"); textBox.Text = awesomeUiResult; 

What do you think should happen? Is it more natural for you to be able to update the text box after the await, or for it to fail because you're now not inside your original context?

Sign up to request clarification or add additional context in comments.

9 Comments

Thanks for your elaborated answer. You said that ConfigureAwait(false): 1. saves time 2. avoids deadlocks. And why doesn't Microsoft encapsulate this method inside the Task class? It looks like the ConfigureAwait(false) has just only the pluses. Is there any minuses?
The minus is that you don't return the SynchronizationContext, and that would be surprising to the end user. I'll edit my answer to explain.
I read your edit hundred times, but didn't understand exactly what do you mean: execute a method which returns a Task where is this method was called? Was it an UI (action method) thread?
@dyatchenko I've added an example. Hope that explains what i ment.
Yes, you're right, I mean the same thing. Thank you very much! I think I got the answers on my questions.
|
2

ASP.NET requires using the same SynchronizationContext for asynchronous operations in the controller, otherwise it blocks the running thread.

Sort of, but not quite. ASP.NET creates a SynchronizationContext for each incoming request, and this context is not tied to a specific thread at all. However, only one thread at a time can enter the context, so if a second thread attempts to enter it while one is already in it, then it will block that thread.

what purpose led Microsoft to use the same SynchronizationContext?

The SynchronizationContext for ASP.NET manages per-request data such as HttpContext.Current, culture, and user identity.

is it really good practice to use the ConfigureAwait(false) wherever is possible?

As a general rule, yes, especially for generic libraries. On ASP.NET, ConfigureAwait(false) doesn't gain you much - in some cases, a minor performance increase. It can be used to avoid the deadlock I mention in my article, but it's far better (especially on ASP.NET) to use async all the way.

4 Comments

Ok. Got you. Thank you very much Stephen! But why is it impossible to use the async operations inside a constructor of the controller? I've asked about it in the another question: stackoverflow.com/questions/28805796
@dyatchenko: Internally, DownloadStringTaskAsync is starting an asynchronous operation in a way very similar to async void.
But why can we use DownloadTaskAsync inside the action method without any problems?
@dyatchenko: Because the action method (asynchronously) waits for it to complete. The constructor does not. ASP.NET sees that the asynchronous operation is not completed yet, so it throws that exception.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.