async/await deadlocking when using a SynchronizationContext - c#

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 the SynchronizationContext.Current
which gets the synchronization context for the current thread.
SynchronizationContext.Current is populated depending on the
environment you are in. The GetAwaiter method of Task looks up for
SynchronizationContext.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 available SynchronizationContext. 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 because SynchronizationContext.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?

I explain this in full in my own blog post, but to reiterate here...
await by default will capture a current "context" and resume its async method on that context. This context is SynchronizationContext.Current unless it is null, in which case it is TaskScheduler.Current.
Deadlocks can occur when you have a one-thread-at-a-time SynchronizationContext and you block on a task representing asynchronous code (e.g., using Task.Wait or Task<T>.Result). Note that it is the blocking that causes the deadlock, not just the SynchronizationContext; the appropriate resolution (almost always) is to make the calling code asynchronous (e.g., replace Task.Wait/Task<T>.Result with await). This is especially true on ASP.NET.
But I still can't understand why the UI thread is blocked?
Your example is running on ASP.NET; there is no UI thread.
what is the available SynchronizationContext?
The current SynchronizationContext should be an instance of AspNetSynchronizationContext, a context that represents an ASP.NET request. This context only allows one thread in at a time.
So, walking through your example:
When a request comes in for this action, CarsSync will start executing within that request context. It proceeds to this line:
var cars = client.GetCarsInAWrongWayAsync().Result;
which is essentially the same as this:
Task<IEnumerable<Car>> carsTask = client.GetCarsInAWrongWayAsync();
var cars = carsTask.Result;
So, it proceeds to call into GetCarsInAWrongWayAsync, which runs until it hits its first await (the GetAsync call). At this point, GetCarsInAWrongWayAsync captures its current context (the ASP.NET request context) and returns an incomplete Task<IEnumerable<Car>>. When the GetAsync download finishes, GetCarsInAWrongWayAsync will resume executing on that ASP.NET request context and (eventually) complete the task it already returned.
However, as soon as GetCarsInAWrongWayAsync returns the incomplete task, CarsSync blocks the current thread, waiting for that task to complete. Note that the current thread is in that ASP.NET request context, so CarsSync will prevent GetCarsInAWrongWayAsync from ever resuming execution, causing the deadlock.
As a final note, GetCarsInAWrongWayAsync is an OK method. It would be better if it used ConfigureAwait(false), but it's not actually wrong. CarsSync is the method causing the deadlock; it's call to Task<T>.Result is wrong. The appropriate fix 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);
}
}

The key point is that some SynchronizationContexts 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 SynchronizationContexts are mutli-threaded and the problem does not occur.

Related

Why does my program freeze when using EdgeJs? [duplicate]

I came across some best practices for asynchronous programming using c#'s async/await keywords (I'm new to c# 5.0).
One of the advices given was the following:
Stability: Know your synchronization contexts
...
Some synchronization contexts are non-reentrant and single-threaded. This means only one unit of work can be executed in the context at a given time. An example of this is the Windows UI thread or the ASP.NET request context.
In these single-threaded synchronization contexts, it’s easy to deadlock yourself. If you spawn off a task from a single-threaded context, then wait for that task in the context, your waiting code may be blocking the background task.
public ActionResult ActionAsync()
{
// DEADLOCK: this blocks on the async task
var data = GetDataAsync().Result;
return View(data);
}
private async Task<string> GetDataAsync()
{
// a very simple async method
var result = await MyWebService.GetDataAsync();
return result.ToString();
}
If I try to dissect it myself, the main thread spawns to a new one in MyWebService.GetDataAsync();, but since the main thread awaits there, it waits on the result in GetDataAsync().Result. Meanwhile, say the data is ready. Why doesn't the main thread continue it's continuation logic and returns a string result from GetDataAsync() ?
Can someone please explain me why there is a deadlock in the above example?
I'm completely clueless about what the problem is ...
Take a look at this example, Stephen has a clear answer for you:
So this is what happens, starting with the top-level method (Button1_Click for UI / MyController.Get for ASP.NET):
The top-level method calls GetJsonAsync (within the UI/ASP.NET context).
GetJsonAsync starts the REST request by calling HttpClient.GetStringAsync (still within the context).
GetStringAsync returns an uncompleted Task, indicating the REST request is not complete.
GetJsonAsync awaits the Task returned by GetStringAsync. The context is captured and will be used to continue running the GetJsonAsync method later. GetJsonAsync returns an uncompleted Task, indicating that the GetJsonAsync method is not complete.
The top-level method synchronously blocks on the Task returned by GetJsonAsync. This blocks the context thread.
... Eventually, the REST request will complete. This completes the Task that was returned by GetStringAsync.
The continuation for GetJsonAsync is now ready to run, and it waits for the context to be available so it can execute in the context.
Deadlock. The top-level method is blocking the context thread, waiting for GetJsonAsync to complete, and GetJsonAsync is waiting for the context to be free so it can complete. For the UI example, the "context" is the UI context; for the ASP.NET example, the "context" is the ASP.NET request context. This type of deadlock can be caused for either "context".
Another link you should read: Await, and UI, and deadlocks! Oh my!
Fact 1: GetDataAsync().Result; will run when the task returned by GetDataAsync() completes, in the meantime it blocks the UI thread
Fact 2: The continuation of the await (return result.ToString()) is queued to the UI thread for execution
Fact 3: The task returned by GetDataAsync() will complete when its queued continuation is run
Fact 4: The queued continuation is never run, because the UI thread is blocked (Fact 1)
Deadlock!
The deadlock can be broken by provided alternatives to avoid Fact 1 or Fact 2.
Fix 1: Avoid 1,4. Instead of blocking the UI thread, use var data = await GetDataAsync(), which allows the UI thread to keep running
Fix 2: Avoid 2,3. Queue the continuation of the await to a different thread that is not blocked, e.g. use var data = Task.Run(GetDataAsync).Result, which will post the continuation to the sync context of a threadpool thread. This allows the task returned by GetDataAsync() to complete.
This is explained really well in an article by Stephen Toub, about half way down where he uses the example of DelayAsync().
I was just fiddling with this issue again in an ASP.NET MVC project. When you want to call async methods from a PartialView, you're not allowed to make the PartialView async. You'll get an exception if you do.
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
Example:
public ActionResult DisplayUserInfo(string userName)
{
// trick to prevent deadlocks of calling async method
// and waiting for on a sync UI thread.
var syncContext = SynchronizationContext.Current;
SynchronizationContext.SetSynchronizationContext(null);
// this is the async call, wait for the result (!)
var model = _asyncService.GetUserInfo(Username).Result;
// restore the context
SynchronizationContext.SetSynchronizationContext(syncContext);
return PartialView("_UserInfo", model);
}
Another main point is that you should not block on Tasks, and use async all the way down to prevent deadlocks. Then it will be all asynchronous not synchronous blocking.
public async Task<ActionResult> ActionAsync()
{
var data = await GetDataAsync();
return View(data);
}
private async Task<string> GetDataAsync()
{
// a very simple async method
var result = await MyWebService.GetDataAsync();
return result.ToString();
}
A work around I came to is to use a Join extension method on the task before asking for the result.
The code look's like this:
public ActionResult ActionAsync()
{
var task = GetDataAsync();
task.Join();
var data = task.Result;
return View(data);
}
Where the join method is:
public static class TaskExtensions
{
public static void Join(this Task task)
{
var currentDispatcher = Dispatcher.CurrentDispatcher;
while (!task.IsCompleted)
{
// Make the dispatcher allow this thread to work on other things
currentDispatcher.Invoke(delegate { }, DispatcherPriority.SystemIdle);
}
}
}
I'm not enough into the domain to see the drawbacks of this solution (if any)

Why do C# asynchronous deadlocks happen? [duplicate]

I came across some best practices for asynchronous programming using c#'s async/await keywords (I'm new to c# 5.0).
One of the advices given was the following:
Stability: Know your synchronization contexts
...
Some synchronization contexts are non-reentrant and single-threaded. This means only one unit of work can be executed in the context at a given time. An example of this is the Windows UI thread or the ASP.NET request context.
In these single-threaded synchronization contexts, it’s easy to deadlock yourself. If you spawn off a task from a single-threaded context, then wait for that task in the context, your waiting code may be blocking the background task.
public ActionResult ActionAsync()
{
// DEADLOCK: this blocks on the async task
var data = GetDataAsync().Result;
return View(data);
}
private async Task<string> GetDataAsync()
{
// a very simple async method
var result = await MyWebService.GetDataAsync();
return result.ToString();
}
If I try to dissect it myself, the main thread spawns to a new one in MyWebService.GetDataAsync();, but since the main thread awaits there, it waits on the result in GetDataAsync().Result. Meanwhile, say the data is ready. Why doesn't the main thread continue it's continuation logic and returns a string result from GetDataAsync() ?
Can someone please explain me why there is a deadlock in the above example?
I'm completely clueless about what the problem is ...
Take a look at this example, Stephen has a clear answer for you:
So this is what happens, starting with the top-level method (Button1_Click for UI / MyController.Get for ASP.NET):
The top-level method calls GetJsonAsync (within the UI/ASP.NET context).
GetJsonAsync starts the REST request by calling HttpClient.GetStringAsync (still within the context).
GetStringAsync returns an uncompleted Task, indicating the REST request is not complete.
GetJsonAsync awaits the Task returned by GetStringAsync. The context is captured and will be used to continue running the GetJsonAsync method later. GetJsonAsync returns an uncompleted Task, indicating that the GetJsonAsync method is not complete.
The top-level method synchronously blocks on the Task returned by GetJsonAsync. This blocks the context thread.
... Eventually, the REST request will complete. This completes the Task that was returned by GetStringAsync.
The continuation for GetJsonAsync is now ready to run, and it waits for the context to be available so it can execute in the context.
Deadlock. The top-level method is blocking the context thread, waiting for GetJsonAsync to complete, and GetJsonAsync is waiting for the context to be free so it can complete. For the UI example, the "context" is the UI context; for the ASP.NET example, the "context" is the ASP.NET request context. This type of deadlock can be caused for either "context".
Another link you should read: Await, and UI, and deadlocks! Oh my!
Fact 1: GetDataAsync().Result; will run when the task returned by GetDataAsync() completes, in the meantime it blocks the UI thread
Fact 2: The continuation of the await (return result.ToString()) is queued to the UI thread for execution
Fact 3: The task returned by GetDataAsync() will complete when its queued continuation is run
Fact 4: The queued continuation is never run, because the UI thread is blocked (Fact 1)
Deadlock!
The deadlock can be broken by provided alternatives to avoid Fact 1 or Fact 2.
Fix 1: Avoid 1,4. Instead of blocking the UI thread, use var data = await GetDataAsync(), which allows the UI thread to keep running
Fix 2: Avoid 2,3. Queue the continuation of the await to a different thread that is not blocked, e.g. use var data = Task.Run(GetDataAsync).Result, which will post the continuation to the sync context of a threadpool thread. This allows the task returned by GetDataAsync() to complete.
This is explained really well in an article by Stephen Toub, about half way down where he uses the example of DelayAsync().
I was just fiddling with this issue again in an ASP.NET MVC project. When you want to call async methods from a PartialView, you're not allowed to make the PartialView async. You'll get an exception if you do.
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
Example:
public ActionResult DisplayUserInfo(string userName)
{
// trick to prevent deadlocks of calling async method
// and waiting for on a sync UI thread.
var syncContext = SynchronizationContext.Current;
SynchronizationContext.SetSynchronizationContext(null);
// this is the async call, wait for the result (!)
var model = _asyncService.GetUserInfo(Username).Result;
// restore the context
SynchronizationContext.SetSynchronizationContext(syncContext);
return PartialView("_UserInfo", model);
}
Another main point is that you should not block on Tasks, and use async all the way down to prevent deadlocks. Then it will be all asynchronous not synchronous blocking.
public async Task<ActionResult> ActionAsync()
{
var data = await GetDataAsync();
return View(data);
}
private async Task<string> GetDataAsync()
{
// a very simple async method
var result = await MyWebService.GetDataAsync();
return result.ToString();
}
A work around I came to is to use a Join extension method on the task before asking for the result.
The code look's like this:
public ActionResult ActionAsync()
{
var task = GetDataAsync();
task.Join();
var data = task.Result;
return View(data);
}
Where the join method is:
public static class TaskExtensions
{
public static void Join(this Task task)
{
var currentDispatcher = Dispatcher.CurrentDispatcher;
while (!task.IsCompleted)
{
// Make the dispatcher allow this thread to work on other things
currentDispatcher.Invoke(delegate { }, DispatcherPriority.SystemIdle);
}
}
}
I'm not enough into the domain to see the drawbacks of this solution (if any)

Asynchronous task deadlock [duplicate]

I came across some best practices for asynchronous programming using c#'s async/await keywords (I'm new to c# 5.0).
One of the advices given was the following:
Stability: Know your synchronization contexts
...
Some synchronization contexts are non-reentrant and single-threaded. This means only one unit of work can be executed in the context at a given time. An example of this is the Windows UI thread or the ASP.NET request context.
In these single-threaded synchronization contexts, it’s easy to deadlock yourself. If you spawn off a task from a single-threaded context, then wait for that task in the context, your waiting code may be blocking the background task.
public ActionResult ActionAsync()
{
// DEADLOCK: this blocks on the async task
var data = GetDataAsync().Result;
return View(data);
}
private async Task<string> GetDataAsync()
{
// a very simple async method
var result = await MyWebService.GetDataAsync();
return result.ToString();
}
If I try to dissect it myself, the main thread spawns to a new one in MyWebService.GetDataAsync();, but since the main thread awaits there, it waits on the result in GetDataAsync().Result. Meanwhile, say the data is ready. Why doesn't the main thread continue it's continuation logic and returns a string result from GetDataAsync() ?
Can someone please explain me why there is a deadlock in the above example?
I'm completely clueless about what the problem is ...
Take a look at this example, Stephen has a clear answer for you:
So this is what happens, starting with the top-level method (Button1_Click for UI / MyController.Get for ASP.NET):
The top-level method calls GetJsonAsync (within the UI/ASP.NET context).
GetJsonAsync starts the REST request by calling HttpClient.GetStringAsync (still within the context).
GetStringAsync returns an uncompleted Task, indicating the REST request is not complete.
GetJsonAsync awaits the Task returned by GetStringAsync. The context is captured and will be used to continue running the GetJsonAsync method later. GetJsonAsync returns an uncompleted Task, indicating that the GetJsonAsync method is not complete.
The top-level method synchronously blocks on the Task returned by GetJsonAsync. This blocks the context thread.
... Eventually, the REST request will complete. This completes the Task that was returned by GetStringAsync.
The continuation for GetJsonAsync is now ready to run, and it waits for the context to be available so it can execute in the context.
Deadlock. The top-level method is blocking the context thread, waiting for GetJsonAsync to complete, and GetJsonAsync is waiting for the context to be free so it can complete. For the UI example, the "context" is the UI context; for the ASP.NET example, the "context" is the ASP.NET request context. This type of deadlock can be caused for either "context".
Another link you should read: Await, and UI, and deadlocks! Oh my!
Fact 1: GetDataAsync().Result; will run when the task returned by GetDataAsync() completes, in the meantime it blocks the UI thread
Fact 2: The continuation of the await (return result.ToString()) is queued to the UI thread for execution
Fact 3: The task returned by GetDataAsync() will complete when its queued continuation is run
Fact 4: The queued continuation is never run, because the UI thread is blocked (Fact 1)
Deadlock!
The deadlock can be broken by provided alternatives to avoid Fact 1 or Fact 2.
Fix 1: Avoid 1,4. Instead of blocking the UI thread, use var data = await GetDataAsync(), which allows the UI thread to keep running
Fix 2: Avoid 2,3. Queue the continuation of the await to a different thread that is not blocked, e.g. use var data = Task.Run(GetDataAsync).Result, which will post the continuation to the sync context of a threadpool thread. This allows the task returned by GetDataAsync() to complete.
This is explained really well in an article by Stephen Toub, about half way down where he uses the example of DelayAsync().
I was just fiddling with this issue again in an ASP.NET MVC project. When you want to call async methods from a PartialView, you're not allowed to make the PartialView async. You'll get an exception if you do.
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
Example:
public ActionResult DisplayUserInfo(string userName)
{
// trick to prevent deadlocks of calling async method
// and waiting for on a sync UI thread.
var syncContext = SynchronizationContext.Current;
SynchronizationContext.SetSynchronizationContext(null);
// this is the async call, wait for the result (!)
var model = _asyncService.GetUserInfo(Username).Result;
// restore the context
SynchronizationContext.SetSynchronizationContext(syncContext);
return PartialView("_UserInfo", model);
}
Another main point is that you should not block on Tasks, and use async all the way down to prevent deadlocks. Then it will be all asynchronous not synchronous blocking.
public async Task<ActionResult> ActionAsync()
{
var data = await GetDataAsync();
return View(data);
}
private async Task<string> GetDataAsync()
{
// a very simple async method
var result = await MyWebService.GetDataAsync();
return result.ToString();
}
A work around I came to is to use a Join extension method on the task before asking for the result.
The code look's like this:
public ActionResult ActionAsync()
{
var task = GetDataAsync();
task.Join();
var data = task.Result;
return View(data);
}
Where the join method is:
public static class TaskExtensions
{
public static void Join(this Task task)
{
var currentDispatcher = Dispatcher.CurrentDispatcher;
while (!task.IsCompleted)
{
// Make the dispatcher allow this thread to work on other things
currentDispatcher.Invoke(delegate { }, DispatcherPriority.SystemIdle);
}
}
}
I'm not enough into the domain to see the drawbacks of this solution (if any)

Different behavior when using ContinueWith or Async-Await

When I use an async-await method (as the example below) in a HttpClient call, this code causes a deadlock. Replacing the async-await method with a t.ContinueWith, it works properly. Why?
public class MyFilter: ActionFilterAttribute {
public override void OnActionExecuting(ActionExecutingContext filterContext) {
var user = _authService.GetUserAsync(username).Result;
}
}
public class AuthService: IAuthService {
public async Task<User> GetUserAsync (string username) {
var jsonUsr = await _httpClientWrp.GetStringAsync(url).ConfigureAwait(false);
return await JsonConvert.DeserializeObjectAsync<User>(jsonUsr);
}
}
This works:
public class HttpClientWrapper : IHttpClient {
public Task<string> GetStringAsync(string url) {
return _client.GetStringAsync(url).ContinueWith(t => {
_log.InfoFormat("Response: {0}", url);
return t.Result;
});
}
This code will deadlock:
public class HttpClientWrapper : IHttpClient {
public async Task<string> GetStringAsync(string url) {
string result = await _client.GetStringAsync(url);
_log.InfoFormat("Response: {0}", url);
return result;
}
}
I describe this deadlock behavior on my blog and in a recent MSDN article.
await will by default schedule its continuation to run inside the current SynchronizationContext, or (if there is no SynchronizationContext) the current TaskScheduler. (Which in this case is the ASP.NET request SynchronizationContext).
The ASP.NET SynchronizationContext represents the request context, and ASP.NET only allows one thread in that context at a time.
So, when the HTTP request completes, it attempts to enter the SynchronizationContext to run InfoFormat. However, there is already a thread in the SynchronizationContext - the one blocked on Result (waiting for the async method to complete).
On the other hand, the default behavior for ContinueWith by default will schedule its continuation to the current TaskScheduler (which in this case is the thread pool TaskScheduler).
As others have noted, it's best to use await "all the way", i.e., don't block on async code. Unfortunately, that's not an option in this case since MVC does not support asynchronous action filters (as a side note, please vote for this support here).
So, your options are to use ConfigureAwait(false) or to just use synchronous methods. In this case, I recommend synchronous methods. ConfigureAwait(false) only works if the Task it's applied to has not already completed, so I recommend that once you use ConfigureAwait(false), you should use it for every await in the method after that point (and in this case, in each method in the call stack). If ConfigureAwait(false) is being used for efficiency reasons, then that's fine (because it's technically optional). In this case, ConfigureAwait(false) would be necessary for correctness reasons, so IMO it creates a maintenance burden. Synchronous methods would be clearer.
An explanation on why your await deadlocks
Your first line:
var user = _authService.GetUserAsync(username).Result;
blocks that thread and the current context while it waits for the result of GetUserAsync.
When using await it attempts to run any remaining statements back on the original context after the task being waited on finishes, which causes deadlocks if the original context is blocked (which is is because of the .Result). It looks like you attempted to preempt this problem by using .ConfigureAwait(false) in GetUserAsync, however by the time that that await is in effect it's too late because another await is encountered first. The actual execution path looks like this:
_authService.GetUserAsync(username)
_httpClientWrp.GetStringAsync(url) // not actually awaiting yet (it needs a result before it can be awaited)
await _client.GetStringAsync(url) // here's the first await that takes effect
When _client.GetStringAsync finishes, the rest of the code can't continue on the original context because that context is blocked.
Why ContinueWith behaves differently
ContinueWith doesn't try to run the other block on the original context (unless you tell it to with an additional parameter) and thus does not suffer from this problem.
This is the difference in behavior that you noticed.
A solution with async
If you still want to use async instead of ContinueWith, you can add the .ConfigureAwait(false) to the first encountered async:
string result = await _client.GetStringAsync(url).ConfigureAwait(false);
which as you most likely already know, tells await not to try to run the remaining code on the original context.
Note for the future
Whenever possible, attempt to not use blocking methods when using async/await. See Preventing a deadlock when calling an async method without using await for avoiding this in the future.
Granted, my answer is only partial, but I'll go ahead with it anyway.
Your Task.ContinueWith(...) call does not specify the scheduler, therefore TaskScheduler.Current will be used - whatever that is at the time. Your await snippet, however, will run on the captured context when the awaited task completes, so the two bits of code may or may not produce similar behaviour - depending on the value of TaskScheduler.Current.
If, say, your first snippet is called from the UI code directly (in which case TaskScheduler.Current == TaskScheduler.Default, the continuation (logging code) will execute on the default TaskScheduler - that is, on the thread pool.
In the second snippet, however, the continuation (logging) will actually run on the UI thread regardless of whether you use ConfigureAwait(false) on the task returned by GetStringAsync, or not. ConfigureAwait(false) will only affect the execution of the code after the call to GetStringAsync is awaited.
Here's something else to illustrate this:
private async void Form1_Load(object sender, EventArgs e)
{
await this.Blah().ConfigureAwait(false);
// InvalidOperationException here.
this.Text = "Oh noes, I'm no longer on the UI thread.";
}
private async Task Blah()
{
await Task.Delay(1000);
this.Text = "Hi, I'm on the UI thread.";
}
The given code sets the Text within Blah() just fine, but it throws a cross-threading exception inside the continuation in the Load handler.
I found the other solutions posted here did not work for me on ASP .NET MVC 5, which still uses synchronous Action Filters. The posted solutions don't guarantee a new thread will be used, they just specify that the same thread does not HAVE to be used.
My solution is to use Task.Factory.StartNew() and specifying TaskCreationOptions.LongRunning in the method call. This ensures a new/different thread is always used, so you can be assured you will never get a deadlock.
So, using the OP example, the following is the solution that works for me:
public class MyFilter: ActionFilterAttribute {
public override void OnActionExecuting(ActionExecutingContext filterContext) {
// var user = _authService.GetUserAsync(username).Result;
// Guarantees a new/different thread will be used to make the enclosed action
// avoiding deadlocking the calling thread
var user = Task.Factory.StartNew(
() => _authService.GetUserAsync(username).Result,
TaskCreationOptions.LongRunning).Result;
}
}

An async/await example that causes a deadlock

I came across some best practices for asynchronous programming using c#'s async/await keywords (I'm new to c# 5.0).
One of the advices given was the following:
Stability: Know your synchronization contexts
...
Some synchronization contexts are non-reentrant and single-threaded. This means only one unit of work can be executed in the context at a given time. An example of this is the Windows UI thread or the ASP.NET request context.
In these single-threaded synchronization contexts, it’s easy to deadlock yourself. If you spawn off a task from a single-threaded context, then wait for that task in the context, your waiting code may be blocking the background task.
public ActionResult ActionAsync()
{
// DEADLOCK: this blocks on the async task
var data = GetDataAsync().Result;
return View(data);
}
private async Task<string> GetDataAsync()
{
// a very simple async method
var result = await MyWebService.GetDataAsync();
return result.ToString();
}
If I try to dissect it myself, the main thread spawns to a new one in MyWebService.GetDataAsync();, but since the main thread awaits there, it waits on the result in GetDataAsync().Result. Meanwhile, say the data is ready. Why doesn't the main thread continue it's continuation logic and returns a string result from GetDataAsync() ?
Can someone please explain me why there is a deadlock in the above example?
I'm completely clueless about what the problem is ...
Take a look at this example, Stephen has a clear answer for you:
So this is what happens, starting with the top-level method (Button1_Click for UI / MyController.Get for ASP.NET):
The top-level method calls GetJsonAsync (within the UI/ASP.NET context).
GetJsonAsync starts the REST request by calling HttpClient.GetStringAsync (still within the context).
GetStringAsync returns an uncompleted Task, indicating the REST request is not complete.
GetJsonAsync awaits the Task returned by GetStringAsync. The context is captured and will be used to continue running the GetJsonAsync method later. GetJsonAsync returns an uncompleted Task, indicating that the GetJsonAsync method is not complete.
The top-level method synchronously blocks on the Task returned by GetJsonAsync. This blocks the context thread.
... Eventually, the REST request will complete. This completes the Task that was returned by GetStringAsync.
The continuation for GetJsonAsync is now ready to run, and it waits for the context to be available so it can execute in the context.
Deadlock. The top-level method is blocking the context thread, waiting for GetJsonAsync to complete, and GetJsonAsync is waiting for the context to be free so it can complete. For the UI example, the "context" is the UI context; for the ASP.NET example, the "context" is the ASP.NET request context. This type of deadlock can be caused for either "context".
Another link you should read: Await, and UI, and deadlocks! Oh my!
Fact 1: GetDataAsync().Result; will run when the task returned by GetDataAsync() completes, in the meantime it blocks the UI thread
Fact 2: The continuation of the await (return result.ToString()) is queued to the UI thread for execution
Fact 3: The task returned by GetDataAsync() will complete when its queued continuation is run
Fact 4: The queued continuation is never run, because the UI thread is blocked (Fact 1)
Deadlock!
The deadlock can be broken by provided alternatives to avoid Fact 1 or Fact 2.
Fix 1: Avoid 1,4. Instead of blocking the UI thread, use var data = await GetDataAsync(), which allows the UI thread to keep running
Fix 2: Avoid 2,3. Queue the continuation of the await to a different thread that is not blocked, e.g. use var data = Task.Run(GetDataAsync).Result, which will post the continuation to the sync context of a threadpool thread. This allows the task returned by GetDataAsync() to complete.
This is explained really well in an article by Stephen Toub, about half way down where he uses the example of DelayAsync().
I was just fiddling with this issue again in an ASP.NET MVC project. When you want to call async methods from a PartialView, you're not allowed to make the PartialView async. You'll get an exception if you do.
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
Example:
public ActionResult DisplayUserInfo(string userName)
{
// trick to prevent deadlocks of calling async method
// and waiting for on a sync UI thread.
var syncContext = SynchronizationContext.Current;
SynchronizationContext.SetSynchronizationContext(null);
// this is the async call, wait for the result (!)
var model = _asyncService.GetUserInfo(Username).Result;
// restore the context
SynchronizationContext.SetSynchronizationContext(syncContext);
return PartialView("_UserInfo", model);
}
Another main point is that you should not block on Tasks, and use async all the way down to prevent deadlocks. Then it will be all asynchronous not synchronous blocking.
public async Task<ActionResult> ActionAsync()
{
var data = await GetDataAsync();
return View(data);
}
private async Task<string> GetDataAsync()
{
// a very simple async method
var result = await MyWebService.GetDataAsync();
return result.ToString();
}
A work around I came to is to use a Join extension method on the task before asking for the result.
The code look's like this:
public ActionResult ActionAsync()
{
var task = GetDataAsync();
task.Join();
var data = task.Result;
return View(data);
}
Where the join method is:
public static class TaskExtensions
{
public static void Join(this Task task)
{
var currentDispatcher = Dispatcher.CurrentDispatcher;
while (!task.IsCompleted)
{
// Make the dispatcher allow this thread to work on other things
currentDispatcher.Invoke(delegate { }, DispatcherPriority.SystemIdle);
}
}
}
I'm not enough into the domain to see the drawbacks of this solution (if any)

Categories

Resources