Page 120 - CSharp/C#
P. 120
public async Task<ActionResult> Index()
{
// Execution on the initially assigned thread
List<Product> products = await dbContext.Products.ToListAsync();
// Execution resumes on a "random" thread from the pool
return View(products);
}
public ActionResult IndexSync()
{
Task<ActionResult> task = Index();
// Block waiting for the result synchronously
ActionResult result = Task.Result;
return result;
}
This is because, by default the awaited task, in this case db.Products.ToListAsync() will capture the
context (in the case of ASP.NET the request context) and try to use it once it has completed.
When the entire call stack is asynchronous there is no problem because, once await is reached
the original thread is release, freeing the request context.
When we block synchronously using Task.Result or Task.Wait() (or other blocking methods) the
original thread is still active and retains the request context. The awaited method still operates
asynchronously and once the callback tries to run, i.e. once the awaited task has returned, it
attempts to obtain the request context.
Therefore the deadlock arises because while the blocking thread with the request context is
waiting for the asynchronous operation to complete, the asynchronous operation is trying to obtain
the request context in order to complete.
ConfigureAwait
By default calls to an awaited task will capture the current context and attempt to resume
execution on the context once complete.
By using ConfigureAwait(false) this can be prevented and deadlocks can be avoided.
public async Task<ActionResult> Index()
{
// Execution on the initially assigned thread
List<Product> products = await dbContext.Products.ToListAsync().ConfigureAwait(false);
// Execution resumes on a "random" thread from the pool without the original request
context
return View(products);
}
public ActionResult IndexSync()
{
Task<ActionResult> task = Index();
https://riptutorial.com/ 66

