According to this link:
When you are awaiting on a method with await keyword, compiler generates bunch of code in behalf of you. One of the purposes of this action is to handle synchronization with the UI thread. The key
component of this feature is theSynchronizationContext.Current
which gets the synchronization context for the current thread.SynchronizationContext.Current
is populated depending on the
environment you are in. TheGetAwaiter
method of Task looks up forSynchronizationContext.Current
. If current synchronization context is not null, the continuation that gets passed to that awaiter will get posted back to that synchronization context.When consuming a method, which uses the new asynchronous language features, in a blocking fashion, you will end up with a deadlock if
you have an availableSynchronizationContext
. When you are consuming such methods in a blocking fashion (waiting on the Task with Wait method or taking the result directly from the Result property of the Task), you will block the main thread at the same time. When eventually the Task completes inside that method in the threadpool, it is going to invoke the continuation to post back to the main thread becauseSynchronizationContext.Current
is available and captured. But there is a problem here: the UI thread is blocked and you have a deadlock!
public class HomeController : Controller
{
public ViewResult CarsSync()
{
SampleAPIClient client = new SampleAPIClient();
var cars = client.GetCarsInAWrongWayAsync().Result;
return View("Index", model: cars);
}
}
public class SampleAPIClient
{
private const string ApiUri = "http://localhost:17257/api/cars";
public async Task<IEnumerable<Car>> GetCarsInAWrongWayAsync()
{
using (var client = new HttpClient())
{
var response = await client.GetAsync(ApiUri);
// Not the best way to handle it but will do the work for demo purposes
response.EnsureSuccessStatusCode();
return await response.Content.ReadAsAsync<IEnumerable<Car>>();
}
}
}
I have trouble understanding the bolded part of the statement above, but when I test the code above, it deadlocks as expected. But I still can't understand why the UI thread is blocked?
In this case, what is the available SynchronizationContext
? Is it the UI thread?
You can use the following simple workaround in the scenario where you want to call an async method from a sync method: Before the call, clear the SynchronizationContext. Do the call, there will be no more deadlock here, wait for it to finish. Restore the SynchronizationContext.
An await expression in an async method doesn't block the current thread while the awaited task is running. Instead, the expression signs up the rest of the method as a continuation and returns control to the caller of the async method. The async and await keywords don't cause additional threads to be created.
The call to the async method starts an asynchronous task. However, because no Await operator is applied, the program continues without waiting for the task to complete. In most cases, that behavior isn't expected.
Because await is only valid inside async functions and modules, which themselves are asynchronous and return promises, the await expression never blocks the main thread and only defers execution of code that actually depends on the result, i.e. anything after the await expression.
The key point is that some SynchronizationContext
s only allow a single thread to run code at the same time. One thread is calling Result
or Wait
. When the async methods wants to enter it can't.
Some SynchronizationContext
s are mutli-threaded and the problem does not occur.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With