I fully explain this in my own blog post , but repeat here ...
await by default capture the current "context" and resume its async method in that context. This context is SynchronizationContext.Current , if it is not null , in which case it is TaskScheduler.Current .
Deadlocks can occur when you have a single-threaded SynchronizationContext time, and you block a task that represents asynchronous code (for example, using Task.Wait or Task<T>.Result ). Note that locking causes a deadlock, not just SynchronizationContext ; the appropriate permission (almost always) is to make the calling code asynchronous (for example, replace Task.Wait / Task<T>.Result with await ). This is especially true for ASP.NET.
But I still can not understand why the UI thread is blocked?
Your example runs on ASP.NET; no user interface thread.
What is the available SynchronizationContext?
The current SynchronizationContext must be an instance of AspNetSynchronizationContext , a context representing an ASP.NET request. This context allows only one thread at a time.
So, following your example:
When a request arrives for this action, CarsSync will begin execution in this request context. It goes to this line:
var cars = client.GetCarsInAWrongWayAsync().Result;
which is essentially the same:
Task<IEnumerable<Car>> carsTask = client.GetCarsInAWrongWayAsync(); var cars = carsTask.Result;
So, he goes on to call GetCarsInAWrongWayAsync , which starts until his first await (call GetAsync ) is GetAsync . At this point, GetCarsInAWrongWayAsync captures its current context (ASP.NET request context) and returns an incomplete Task<IEnumerable<Car>> . When the GetAsync download completes, GetCarsInAWrongWayAsync resume executing the ASP.NET request in this context and (eventually) execute the job that it already returned.
However, as soon as GetCarsInAWrongWayAsync returns an incomplete task, CarsSync blocks the current thread, waiting for the completion of this task. Note that the current thread is in the context of an ASP.NET request, so CarsSync prevent CarsSync from GetCarsInAWrongWayAsync , causing a deadlock.
As a final note, GetCarsInAWrongWayAsync is an OK method. It would be better if he used ConfigureAwait(false) , but in fact it is not. CarsSync - a method that causes a dead end; the call to Task<T>.Result . The relevant solution is to change CarsSync :
public class HomeController : Controller { public async Task<ViewResult> CarsSync() { SampleAPIClient client = new SampleAPIClient(); var cars = await client.GetCarsInAWrongWayAsync(); return View("Index", model: cars); } }