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)
Related
I don't quite understand the difference between Task.Wait and await.
I have something similar to the following functions in a ASP.NET WebAPI service:
public class TestController : ApiController
{
public static async Task<string> Foo()
{
await Task.Delay(1).ConfigureAwait(false);
return "";
}
public async static Task<string> Bar()
{
return await Foo();
}
public async static Task<string> Ros()
{
return await Bar();
}
// GET api/test
public IEnumerable<string> Get()
{
Task.WaitAll(Enumerable.Range(0, 10).Select(x => Ros()).ToArray());
return new string[] { "value1", "value2" }; // This will never execute
}
}
Where Get will deadlock.
What could cause this? Why doesn't this cause a problem when I use a blocking wait rather than await Task.Delay?
Wait and await - while similar conceptually - are actually completely different.
Wait will synchronously block until the task completes. So the current thread is literally blocked waiting for the task to complete. As a general rule, you should use "async all the way down"; that is, don't block on async code. On my blog, I go into the details of how blocking in asynchronous code causes deadlock.
await will asynchronously wait until the task completes. This means the current method is "paused" (its state is captured) and the method returns an incomplete task to its caller. Later, when the await expression completes, the remainder of the method is scheduled as a continuation.
You also mentioned a "cooperative block", by which I assume you mean a task that you're Waiting on may execute on the waiting thread. There are situations where this can happen, but it's an optimization. There are many situations where it can't happen, like if the task is for another scheduler, or if it's already started or if it's a non-code task (such as in your code example: Wait cannot execute the Delay task inline because there's no code for it).
You may find my async / await intro helpful.
Based on what I read from different sources:
An await expression does not block the thread on which it is executing. Instead, it causes the compiler to sign up the rest of the async method as a continuation on the awaited task. Control then returns to the caller of the async method. When the task completes, it invokes its continuation, and execution of the async method resumes where it left off.
To wait for a single task to complete, you can call its Task.Wait method. A call to the Wait method blocks the calling thread until the single class instance has completed execution. The parameterless Wait() method is used to wait unconditionally until a task completes. The task simulates work by calling the Thread.Sleep method to sleep for two seconds.
This article is also a good read.
Some important facts were not given in other answers:
async/await is more complex at CIL level and thus costs memory and CPU time.
Any task can be canceled if the waiting time is unacceptable.
In the case of async/await we do not have a handler for such a task to cancel it or monitoring it.
Using Task is more flexible than async/await.
Any sync functionality can by wrapped by async.
public async Task<ActionResult> DoAsync(long id)
{
return await Task.Run(() => { return DoSync(id); } );
}
async/await generate many problems. We do not know if await statement will be reached without runtime and context debugging. If first await is not reached, everything is blocked. Sometimes even when await seems to be reached, still everything is blocked:
https://github.com/dotnet/runtime/issues/36063
I do not see why I must live with the code duplication for sync and async method or using hacks.
Conclusion: Creating Tasks manually and controlling them is much better. Handler to Task gives more control. We can monitor Tasks and manage them:
https://github.com/lsmolinski/MonitoredQueueBackgroundWorkItem
Sorry for my english.
I recently read Stephen Cleary's post regarding possible deadlocks that occur when we call async code in synchronous methods here: https://blog.stephencleary.com/2012/07/dont-block-on-async-code.html
Regarding a slightly modified example here (all I added was a writeline code):
// My "library" method.
public static async Task<JObject> GetJsonAsync(Uri uri)
{
Console.WriteLine("Before await");
using (var client = new HttpClient())
{
var jsonString = await client.GetStringAsync(uri).ConfigureAwait(true);
return JObject.Parse(jsonString);
}
}
// My "top-level" method.
public void Button1_Click(...)
{
var jsonTask = GetJsonAsync(...);
textBox1.Text = jsonTask.Result;
}
His explanation is that the top-level method is blocking the UI thread waiting for GetJsonAsync to finish whereas GetJsonAsync is waiting for the UI thread to be freed so it can finish executing.
My question is, isn't GetJsonAsync already on the UI thread? Why would it need to wait for it to be freed? According to this post here calling async functions don't necessarily create another thread for the method to execute on. So how would it cause an issue with the UI thread if GetJsonAsync was executing on the UI thread the entire time? Like when Console.WriteLine() is executed where is this done, if not on the UI thread? I feel like I'm missing something here but don't know what.
Clarification: At which point does the execution leave the UI thread/context and need to return? There so much discussion about needing to return but never when it leaves the thread/context.
What I'm asking is, where is GetJsonAsync executing when it's called from Button1_Click if this call doesn't create a new thread for it to execute on. Before the await within GetJsonAsync, isn't it executing the Console.WriteLine(...) within the UI context still?
I recommend reading my async intro. In summary:
Every asynchronous method begins executing synchronously. This code:
public void Button1_Click(...)
{
var jsonTask = GetJsonAsync(...);
textBox1.Text = jsonTask.Result;
}
calls GetJsonAsync on the UI thread, and it does begin executing on the UI thread. It executes Console.WriteLine on the UI thread, news up a client on the UI thread, and even calls GetStringAsync on the UI thread. It gets a task back from that method, and then awaits it (I'm ignoring the ConfigureAwait(true) for simplicity).
The await is the point at which things may become asynchronous. The task isn't complete (i.e., the client hasn't received the string yet), so GetJsonAsync returns an incomplete task to its caller. Then Button1_Click blocks the UI thread, waiting for that task to complete (by calling .Result).
So, the state is currently GetJsonAsync is no longer running on the UI thread. It is not actually "running" anywhere.
Later, when that string result arrives, the task that was returned from GetStringAsync is completed, and GetJsonAsync needs to resume executing. It's not already on the UI thread; it's not anywhere at the moment. Since the await captured a UI context, it will attempt to resume on that context (on the UI thread).
I have the following in my REST API server(using OWIN):
[ResponseType(typeof(Session))]
[Route("api/v1/async")]
public Session async_status()
{
AsyncStatus();
return new Session() {...};
}
private async Task<int> AsyncStatus()
{
await SyncMethod();
return 42;
}
private Task<int> SyncMethod()
{
CopyFileThatTakesALongTime(....);
// OR
Thread.Sleep(60000);
return Task.FromResult(42);
}
Why dies it block and the client does not get the desired asynchronous-ness. I I would expect that when calling:
await SyncMethod();
The caller would be freed and unblocked while the rest of the sleep would be executed. What am i missing?
P.S: I can easily fix it by replacing this:
AsyncStatus();
With:
Task.Run(() => AsyncStatus() );
The reason i am not doing that is because of this reading:
https://blog.stephencleary.com/2013/11/taskrun-etiquette-examples-using.html
In other words if it's bad to use Task.Run() in a web server as in the above article but you can't use async/await, what is the solution?
An async method only becomes asynchronous when asynchronous work starts.
That is, all of your code will run synchronously until something truly asynchronous starts. Once some truly asynchronous work starts, a Task is returned up the call stack, which then can be awaited (or not) by the calling methods.
The implication of this is that an async method could still hold up the request if you're doing a lot of synchronous work before the asynchronous work. Your example shows that perfectly. Let's break down exactly what a thread is doing:
Start running async_status()
Jump to AsyncStatus()
At await SyncMethod() the thread jumps to SyncMethod().
The thread halts at Thread.Sleep(60000) for 60 seconds.
SyncMethod() returns a complete Task.
Because there is nothing to await, AsyncStatus() continues synchronously and returns a value.
async_status() returns and thus the request.
Thread.Sleep(60000) can be replaced with any processor-intensive task (or any long-running non-async task) and the result would be the same if it happens before the first await in that method.
This is also described in Microsoft's documentation under the heading What happens in an async method. Notice point 3:
Something happens in GetStringAsync that suspends its progress. Perhaps it must wait for a website to download or some other blocking activity. To avoid blocking resources, GetStringAsync yields control to its caller, AccessTheWebAsync.
Control only returns to the calling method when a Task is returned, which only happens when an asynchronous task is actually started. In your example, there isn't any asynchronous task happening. If you added one (even just await Task.Delay(1)) you would see different results. For example, this will return right away:
[ResponseType(typeof(Session))]
[Route("api/v1/async")]
public Session async_status()
{
AsyncStatus();
return new Session() {...};
}
private async Task<int> AsyncStatus()
{
await SyncMethod();
return 42;
}
private async Task<int> SyncMethod()
{
await Task.Delay(1);
Thread.Sleep(60000);
return 42;
}
At await Task.Delay(1) (or any truly asynchronous work):
SyncMethod() returns an incomplete Task to AsyncStatus()
AsyncStatus() returns an incomplete Task to async_status().
That Task is not awaited, so execution of async_status() continues
To answer your question:
You asked:
if it's bad to use Task.Run() in a web server as in the above article but you can't use async/await, what is the solution?
You mentioned the Task.Run Etiquette article. Read his conclusion at the end:
Do not use it just to “provide something awaitable for my async method to use”.
Your purpose here is not to just "provide something awaitable", but to start a background job and not make the client wait until it's done. That's different.
So yes, you could actually use Task.Run(). However, as noted in Stephen Cleary's Fire and Forget on ASP.NET article, the problem with doing it this way is that ASP.NET does not keep track of that background task. So if it's long-running, then recycling the app pool could immediately kill that work. His article describes better methods of performing background work that you don't want to hold up the return of the request.
When doing await the Task could or could not be executed on the same thread. Usually, we just need to take this fact into design consideration. But I would like to do a test specifically for the thread change scenario - how do I ensure the rest of the async method (after await) is run on a different thread?
Possible solution:
[Test]
public void TestForThreadChange()
{
int threadIdBefore, threadIdAfter = 0;
async Task AsyncFunction()
{
Console.WriteLine($"before - {threadIdBefore = Thread.CurrentThread.ManagedThreadId}");
await Task.Yield();
Console.WriteLine($"after - {threadIdAfter = Thread.CurrentThread.ManagedThreadId}");
}
AsyncFunction().Wait();
Assert.That(threadIdBefore, Is.Not.EqualTo(threadIdAfter));
}
how do I ensure the rest of the async method (after await) is run on a different thread?
First, ensure that you have call the method being tested from a thread-pool context. Some unit test frameworks do provide a single-threaded context. If that is the case, you'll want to wrap your testing code in an await Task.Run(() => ...).
Then, mock the method being awaited and ensure it does an await Task.Yield(); this will ensure the await in the method under test will get an incomplete task, causing it to resume on a thread pool thread.
If you want to be absolutely sure that it resumes on a different thread pool thread, you'll need to block your unit test method on the method being tested, so that the current thread pool thread is blocked and not used to resume the method being tested.
You can't. Async pattern is designed to simplify the async (duh) code at the expense of some control. If you need to control thread creation, use Thread/ThreadPool.
I don't quite understand the difference between Task.Wait and await.
I have something similar to the following functions in a ASP.NET WebAPI service:
public class TestController : ApiController
{
public static async Task<string> Foo()
{
await Task.Delay(1).ConfigureAwait(false);
return "";
}
public async static Task<string> Bar()
{
return await Foo();
}
public async static Task<string> Ros()
{
return await Bar();
}
// GET api/test
public IEnumerable<string> Get()
{
Task.WaitAll(Enumerable.Range(0, 10).Select(x => Ros()).ToArray());
return new string[] { "value1", "value2" }; // This will never execute
}
}
Where Get will deadlock.
What could cause this? Why doesn't this cause a problem when I use a blocking wait rather than await Task.Delay?
Wait and await - while similar conceptually - are actually completely different.
Wait will synchronously block until the task completes. So the current thread is literally blocked waiting for the task to complete. As a general rule, you should use "async all the way down"; that is, don't block on async code. On my blog, I go into the details of how blocking in asynchronous code causes deadlock.
await will asynchronously wait until the task completes. This means the current method is "paused" (its state is captured) and the method returns an incomplete task to its caller. Later, when the await expression completes, the remainder of the method is scheduled as a continuation.
You also mentioned a "cooperative block", by which I assume you mean a task that you're Waiting on may execute on the waiting thread. There are situations where this can happen, but it's an optimization. There are many situations where it can't happen, like if the task is for another scheduler, or if it's already started or if it's a non-code task (such as in your code example: Wait cannot execute the Delay task inline because there's no code for it).
You may find my async / await intro helpful.
Based on what I read from different sources:
An await expression does not block the thread on which it is executing. Instead, it causes the compiler to sign up the rest of the async method as a continuation on the awaited task. Control then returns to the caller of the async method. When the task completes, it invokes its continuation, and execution of the async method resumes where it left off.
To wait for a single task to complete, you can call its Task.Wait method. A call to the Wait method blocks the calling thread until the single class instance has completed execution. The parameterless Wait() method is used to wait unconditionally until a task completes. The task simulates work by calling the Thread.Sleep method to sleep for two seconds.
This article is also a good read.
Some important facts were not given in other answers:
async/await is more complex at CIL level and thus costs memory and CPU time.
Any task can be canceled if the waiting time is unacceptable.
In the case of async/await we do not have a handler for such a task to cancel it or monitoring it.
Using Task is more flexible than async/await.
Any sync functionality can by wrapped by async.
public async Task<ActionResult> DoAsync(long id)
{
return await Task.Run(() => { return DoSync(id); } );
}
async/await generate many problems. We do not know if await statement will be reached without runtime and context debugging. If first await is not reached, everything is blocked. Sometimes even when await seems to be reached, still everything is blocked:
https://github.com/dotnet/runtime/issues/36063
I do not see why I must live with the code duplication for sync and async method or using hacks.
Conclusion: Creating Tasks manually and controlling them is much better. Handler to Task gives more control. We can monitor Tasks and manage them:
https://github.com/lsmolinski/MonitoredQueueBackgroundWorkItem
Sorry for my english.