Can this "Synchronous" Code Cause Deadlocks? - c#

My company has a Nuget Package they wrote that can do various common tasks easily for you. One of which is making HTTP requests. Normally I always make my HTTP requests asynchronous, however in this Nuget package is the following code:
protected T GetRequest<T>(string requestUri)
{
// Call the async method within a task to run it synchronously
return Task.Run(() => GetRequestAsync<T>(requestUri)).Result;
}
Which calls this function:
protected async Task<T> GetRequestAsync<T>(string requestUri)
{
// Set up the uri and the client
string uri = ParseUri(requestUri);
var client = ConfigureClient();
// Call the web api
var response = await client.GetAsync(uri);
// Process the response
return await ProcessResponse<T>(response);
}
My question is, is this code really running synchronously by just wrapping the GetRequestAsync(requestUri) inside a Task.Run and calling .Result on the returned task? This seems like a deadlock waiting to happen, and we are seeing issues in areas of our app that utilize this function when running at higher loads.

Accessing Task.Result will block the current thread until the Task is complete, so it is not asynchronous.
As for deadlocks, this shouldn't happen, as Task.Run uses another thread for GetRequestAsync, which is not being blocked by the call to Result.

The reason that will not cause a deadlock is because Task.Run will push the delegate to be executed in a threadpool thread. Threadpool threads has no SynchronizationContext therefore no deadlock happens as there is no sync context to lock on between the async method GetRequestAsync and the caller. Same as you could have called .Result directly on the actual async method as well within Task.Run() block and that would not have caused a deadlock either.
Very inefficient though as you freeze 1 thread ie. 1 Core in the CPU do nothing but wait for the async method and I/O calls within it to complete. That s probably why you see a freeze in high load scenarios.
If you have a sync/async deadlock issue due to capturing sync context and blocking on async call, the deadlock will happen irrespective of load on a single call..

This won't cause a deadlock. But it's surely a resources wasting as one of the threads may be blocked.
The deadlock though may be possible if GetRequest looked like this:
protected T GetRequest<T>(string requestUri)
{
var task = GetRequestAsync<T>(requestUri);
return task.Result;
// or
// return GetRequestAsync<T>(requestUri).Result;
}
In example above you can see that I call GetRequestAsync within the current thread. Let's give the thread a number 0. Consider this line from the GetRequestAsync - await client.GetAsync(uri). .GetAsync is executed by a thread 1. After .GetAsync is done, default task scheduler is trying to return execution flow to the thread that executed the line - to the thread 0. But the thread that executed the line (0) is blocked right now as after we executed GetRequestAsync(), we are blocking it (thread 0) with task.Result. Hence our thread 0 remains blocked as it cannot proceed with execution of GetRequestAsync after await client.GetAsync(uri) is done nor it can give us the Result.
It's a pretty common mistake and I suppose you meant this one when asked about the deadlock. Your code is not causing it because you are executing GetRequestAsync from within another thread.

Related

What is the difference between discarding DoAwait() VS Task.Run(() => DoAwait())

So let's assume I have the following async method:
public async Task DoAsync()
{
await Task.Delay(10000);
ExecuteContinuation();
}
Now I want to execute this task in a fire-and-forget way from ASP.NET (non-Core) Controller method (I know that's not the best idea, but that's not the scope of the question).
Therefore I simply discard returned task:
_ = DoAsync();
That leads to an interesting result - ExecuteContinuation() is never executed, breakpoint is never hit.
Now if I wrap it in Task.Run, everything works just fine:
_ = Task.Run(() => DoAsync());
Question is - why such behavior happens, and why ExecuteContinuation() is never executed without Task.Run?
As far as I remember, Task.Run forces the task to execute on a thread pool ignoring SynchronizationContext, but does it make any difference, considering that I never await returned task?
Another thing - _ = DoAsync(); works as expected if I do same with ASP.NET Core. I suppose because there is no SynchronizationContext, but again - I don't know why that matters in this case.
As far as I remember, Task.Run forces the task to execute on a thread pool ignoring SynchronizationContext, but does it make any difference, considering that I never await returned task?
Yes. It makes a difference for the await inside DoAsync. When calling it directly, it will capture the current SynchronizationContext (representing the current request). Later when that await resumes executing, it will attempt to resume on the captured context for the request that has already completed. The resulting behavior is undefined.
When calling DoAsync from Task.Run, no context is captured and DoAsync may resume executing on any thread pool thread without the context for the completed request.
Note that since this is fire-and-forget, it is inevitable that DoAsync will occasionally not finish, even when using Task.Run. Your system as a whole must properly handle that scenario. The proper solution for fire-and-forget code is a distributed architecture.

Is there a way to apply .ConfigureAwait(false) to a whole task subtree?

I have a class that runs thousands of async tasks in parallel which is much greater than amount of worker threads. If I don't do .ConfigureAwait(false) on await calls then I get much lower performance but attaching .ConfigureAwait(false) to every single await call is tedious and reduces code readability. I'm looking for a way to specify some kind of null context in a function that spawns those tasks so that every await call inside them automatically doesn't care about SynchronizationContext. Is it possible?
Update: I've done some googling and it looks like once I'm already inside a function with .ConfigureAwait(false) the current SynchronizationContext will be null and I don't need to care about it in child function calls. Is that correct?
Yes, you can. By resetting the current SynchronizationContext.
At the top level async method do this:
async Task TopLevelAsync()
{
var syncContext = SynchronizationContext.Current;
SynchronizationContext.SetSynchronizationContext(null);
try
{
// no need for ConfigureAwait(false)
await SubTask1Async();
await SubTask2Async();
}
finally
{
SynchronizationContext.SetSynchronizationContext(syncContext);
}
}
once I'm already inside a function with .ConfigureAwait(false) the current SynchronizationContext will be null and I don't need to care about it in child function calls. Is that correct?
Unfortunately, it's not. It's a dangerous assumption. And here is why:
// in a top-level async method..
await FooAsync().ConfigureAwait(false);
async Task FooAsync()
{
var result = await BarAsync().ConfigureAwait(false);
// we are inside the ConfigureAwait(false), or in the
// continuation after ConfigureAwait(false), so at this
// point the SynchronizationContext must be null, right?
// No it's not.
}
Task<bool> BarAsync()
{
return Task.FromResult(true);
}
From the example above the BarAsync completes synchronously, that means the state machine behind the FooAsync method continues it's execution immediately without re-scheduling. Without re-scheduling logic, the ConfigureAwait(false) does not taken into an account, so the SynchronizationContext is never reset.
it looks like once I'm already inside a function with .ConfigureAwait(false) the current SynchronizationContext will be null and I don't need to care about it in child function calls. Is that correct?
Well, the actual situation is:
You're inside an asynchronous function.
You start a task.
You do an await (configured with false) on that task before it completes.
So, it's not a great situation to depend on. Specifically, if the task completes quickly (before your configured await is hit), then you will still be on the original context. Note that this can happen even if you don't expect it to; for example, mobile devices are extremely aggressive about caching web requests.
To "step out" of the synchronization context, just wrap it in a Task.Run - this uses a thread pool thread very briefly, and executes all the descendant code in that thread pool context.

async/await. Where is continuation of awaitable part of method performed?

I am really curious how async/await enables your program not to be halted.
I really like the way how Stephen Cleary explains async/await: "I like to think of "await" as an "asynchronous wait". That is to say, the async method pauses until the awaitable is complete(so it waits), but the actual thread is not blocked (so it's asynchornous)."
I've read that async method works synchronously till compilator meets await keywords. Well. If compilator cannot figure out awaitable, then compilator queues the awaitable and yield control to the method that called method AccessTheWebAsync. OK.
Inside the caller (the event handler in this example), the processing pattern continues. The caller might do other work that doesn't depend on the result from AccessTheWebAsync before awaiting that result, or the caller might await immediately. The event handler is waiting for AccessTheWebAsync, and AccessTheWebAsync is waiting for GetStringAsync. Let's see an msdn example:
async Task<int> AccessTheWebAsync()
{
// You need to add a reference to System.Net.Http to declare client.
HttpClient client = new HttpClient();
// GetStringAsync returns a Task<string>. That means that when you await the
// task you'll get a string (urlContents).
Task<string> getStringTask = client.GetStringAsync("http://msdn.microsoft.com");
// You can do work here that doesn't rely on the string from GetStringAsync.
DoIndependentWork();
// The await operator suspends AccessTheWebAsync.
// - AccessTheWebAsync can't continue until getStringTask is complete.
// - Meanwhile, control returns to the caller of AccessTheWebAsync.
// - Control resumes here when getStringTask is complete.
// - The await operator then retrieves the string result from getStringTask.
string urlContents = await getStringTask;
// The return statement specifies an integer result.
// Any methods that are awaiting AccessTheWebAsync retrieve the length value.
return urlContents.Length;
}
Another article from msdn blog says that async/await does not create new thread or use other threads from thread pool. OK.
My questions:
Where does async/await execute awaitable code(in our example downloading a web site) cause control yields to the next row of code of our program and program just asks result of Task<string> getStringTask? We know that no new threads, no thread pool are not used.
Am I right in my silly assumption that CLR just switches the current executable code and awaitable part of the method between each other in scope of one thread? But changing the order of addends does not change the sum and UI might be blocked for some unnoticeable time.
Where does async/await execute awaitable code(in our example downloading a web site) cause control yields to the next row of code of our program and program just asks result of Task getStringTask? We know that no new threads, no thread pool are not used.
If the operation is truly asynchronous, then there's no code to "execute". You can think of it as all being handled via callbacks; the HTTP request is sent (synchronously) and then the HttpClient registers a callback that will complete the Task<string>. When the download completes, the callback is invoked, completing the task. It's a bit more complex than this, but that's the general idea.
I have a blog post that goes into more detail on how asynchronous operations can be threadless.
Am I right in my silly assumption that CLR just switches the current executable code and awaitable part of the method between each other in scope of one thread?
That's a partially true mental model, but it's incomplete. For one thing, when an async method resumes, its (former) call stack is not resumed along with it. So async/await are very different than fibers or co-routines, even though they can be used to accomplish similar things.
Instead of thinking of await as "switch to other code", think of it as "return an incomplete task". If the calling method also calls await, then it also returns an incomplete task, etc. Eventually, you'll either return an incomplete task to a framework (e.g., ASP.NET MVC/WebAPI/SignalR, or a unit test runner); or you'll have an async void method (e.g., UI event handler).
While the operation is in progress, you end up with a "stack" of task objects. Not a real stack, just a dependency tree. Each async method is represented by a task instance, and they're all waiting for that asynchronous operation to complete.
Where is continuation of awaitable part of method performed?
When awaiting a task, await will - by default - resume its async method on a captured context. This context is SynchronizationContext.Current unless it is null, in which case it is TaskScheduler.Current. In practice, this means that an async method running on a UI thread will resume on that UI thread; an async method handling an ASP.NET request will resume handling that same ASP.NET request (possibly on a different thread); and in most other cases the async method will resume on a thread pool thread.
In the example code for your question, GetStringAsync will return an incomplete task. When the download completes, that task will complete. So, when AccessTheWebAsync calls await on that download task, (assuming the download hasn't already finished) it will capture its current context and then return an incomplete task from AccessTheWebAsync.
When the download task completes, the continuation of AccessTheWebAsync will be scheduled to that context (UI thread, ASP.NET request, thread pool, ...), and it will extract the Length of the result while executing in that context. When the AccessTheWebAsync method returns, it sets the result of the task previously returned from AccessTheWebAsync. This in turn will resume the next method, etc.
In general the continuation (the part of your method after await) can run anywhere. In practice it tends to run on the UI thread (e.g. in a Windows application) or on the thread pool (e.g. in an ASP .NET server). It can also run synchronously on the caller thread in some cases ... really it depends on what kind of API you're calling and what synchronization context is being used.
The blog article you linked does not say that continuations are not run on thread pool threads, it merely says that marking a method as async does not magically cause invocations of the method to run on a separate thread or on the thread pool.
That is, they're just trying to tell you that if you have a method void Foo() { Console.WriteLine(); }, changing that to async Task Foo() { Console.WriteLine(); } doesn't suddenly cause an invocation of Foo(); to behave any differently at all – it'll still be executed synchronously.
If by "awaitable code" you mean the actual asynchronous operation, then you need to realize that it "executes" outside of the CPU so there's no thread needed and no code to run.
For example when you download a web page, most of the operation happens when your server sends and receives data from the web server. There's no code to execute while this happens. That's the reason you can "take over" the thread and do other stuff (other CPU operations) before awaiting the Task to get the actual result.
So to your questions:
It "executes" outside of the CPU (so it's not really executed). That could mean the network driver, a remote server, etc. (mostly I/O).
No. Truly asynchronous operations don't need to be executed by the CLR. They are only started and completed in the future.
A simple example is Task.Delay which creates a task that completes after an interval:
var delay = Task.Delay(TimeSpan.FromSeconds(30));
// do stuff
await delay;
Task.Delay internally creates and sets a System.Threading.Timer that will execute a callback after the interval and complete the task. System.Threading.Timer doesn't need a thread, it uses the system clock. So you have "awaitable code" that "executes" for 30 seconds but nothing actually happens in that time. The operation started and will complete 30 seconds in the future.

What really happens when call async method?

I try to understand why is better using the 'Async' method than using simple old synchronous way.
There is small issue that I don't understand.
On the synchronous way:
I have some thread that call method FileStream.Read(...).
Because calling this method is synchronous so the calling thread will wait until the IRP (I/O request packet) will signal that this Io request is finish.
Until the IRP will return ==> this thread will suspend ( sleep ).
On the A-synchronous way:
I have some thread (Task .. lets call this thread 'TheadAsync01') that calls method FileStream.ReadAsync(...).
Because calling this method is A-Synchronous so the calling thread will not wait until the IRP (I/O request packet) will signal that this IO request is finish; and this calling thread will continue to his next action.
Now, When the IRP will signal that this IO request is finish what happened?
(The thread TheadAsync01 is now doing something else and can't continue the work with what the 'FileStream.ReadAsync' return now.)
Is other thread will continue the continue the next action with the return value of the ReadAsync?
What I don't understand here?
The reason it bothers you is this mistaken assumption:
The thread TheadAsync01 is now doing something else and can't continue
the work with what the 'FileStream.ReadAsync' return now.
In a typical application I/O is by far the most time-consuming task.
When TPL is used correctly, threads are not blocked by time-consuming operations. Instead, everything time-consuming (in other words, any I/O) is delegated via await. So when your IRP signals, the thread will either be free from work, or will be free very soon.
If there's some heavy calculation (something time-consuming which is not I/O), you need to plan accordingly, for example run it on a dedicated thread.
The function ReadAsync immediately returns a value, namely a Task object. Somewhere you should do something with the return value. The canonical way is to use await:
await FileStream.ReadAsync(...)
This will ensure that the calling site will not continue with operation until ReadAsync has completed its job. If you want to do something in the meantime you could await the task object later or you can manually deal with the task object.
If you just call ReadAsync, ignoring the returned task object, doing nothing with it, then your reading is mostly an expensive no-op.
When a ***Async method returns a Task or Task you use this to track the running of the asynchronous operation. You can make the call behave synchronously with respect to the calling code by calling .Wait() on the task. Alternatively, as of .Net 4.5 you can await the task.
e.g:
private async void DoFileRead(...)
{
var result = await fileStream.ReadAsync(...);
// Do follow on tasks
}
In this scenario any follow on code would be wrapped in a continuation by the compiler and executed when the async call completed. One requirement of using the async keyword is to mark the calling method with the async keyword (see the example above).

c# async runs single threaded?

I was reading http://msdn.microsoft.com/en-US/library/vstudio/hh191443.aspx.
Example code:
async Task<int> AccessTheWebAsync()
{
// You need to add a reference to System.Net.Http to declare client.
HttpClient client = new HttpClient();
// GetStringAsync returns a Task<string>. That means that when you await the
// task you'll get a string (urlContents).
Task<string> getStringTask = client.GetStringAsync("http://msdn.microsoft.com");
// You can do work here that doesn't rely on the string from GetStringAsync.
DoIndependentWork();
// The await operator suspends AccessTheWebAsync.
// - AccessTheWebAsync can't continue until getStringTask is complete.
// - Meanwhile, control returns to the caller of AccessTheWebAsync.
// - Control resumes here when getStringTask is complete.
// - The await operator then retrieves the string result from getStringTask.
string urlContents = await getStringTask;
// The return statement specifies an integer result.
// Any methods that are awaiting AccessTheWebAsync retrieve the length value.
return urlContents.Length;
}
The page also says:
The async and await keywords don't cause additional threads to be created. Async methods don't require multithreading because an async method doesn't run on its own thread
Does this "no additional thread is created" applies within scope of the method marked as async?
I would imagine in order for both GetStringAsync and AccessTheWebAsync to be running at the same time (otherwise GetStringAsync will never finish as AccessTheWebAsync has control now), eventually GetStringAsync has to run on a different thread from AccessTheWebAsync's thread.
To me, writing async method is only useful at not adding more thread when the method it awaits is also async (which already use extra thread to do its own thing in parallel)
Is my understanding correct?
This is the key to the power of async. GetStringAsync and other naturally-asynchronous operations do not require a thread. GetStringAsync just sends out the HTTP request and registers a callback to run when the server replies. There's no need for a thread just to wait for the server to respond.
In reality, the thread pool is used just a tiny bit. In the example above, the callback registered by GetStringAsync will execute on a thread pool thread, but all it does is notify AccessTheWebAsync that it can continue executing.
I have an async intro blog post you may find helpful.
I would imagine in order for both GetStringAsync and AccessTheWebAsync to be running at the same time ...
They don't run at the same time (at least not in the way you're thinking). Now, if HttpClient.GetStringAsync itself
started work on a different thread, then that code could be running at the same time, -or-
Had an await (with ConfigureAwait(false)), then the remainder of the work in that method would be scheduled on to the Thread pool thread (and so that code could be running at the same time.
The point is that declaring a method as async and/or using await, doesn't cause the method that that you are authoring to be run on a separate thread (you would have to do that explicitly).
Note:
Without having the code (or documentation) of another asynchronous method, you don't really know how much of it is run synchronously. It doesn't really start being asynchronous until that method does an await or explicitly starts work on another thread (usually by starting a new Task)
There is a thread that is taken from the thread pool for the operation if that is necessary. In case of a console (not discussing the UI thread or IO threads) the current thread goes in the console and another thread is taken for the execution, and this new thread is the one that executes the remaining of the operation.
What the author is saying here, is that simply using the async and await keywords somewhere does not cause the current method to run on a different thread. Let's investigate what happens in the code provided.
async Task<int> AccessTheWebAsync()
{
// You need to add a reference to System.Net.Http to declare client.
HttpClient client = new HttpClient();
// GetStringAsync returns a Task<string>. That means that when you await the
// task you'll get a string (urlContents).
Task<string> getStringTask = client.GetStringAsync("http://msdn.microsoft.com");
Everything up to this point has executed in a single thread. At this point, the client.GetStringAsync call could spin off a new thread (although this is not guarenteed for any async method). It should be noted though that the GetStringAsync method essentially starts up on this current thread (such as actually making the call), but reading the response will execute in a different thread once it has returned. This is represented by it returning a Task object. The Task may represent an already completed bit of work, a currently executing secondary thread, or a unit of work that is simply scheduled to execute later.
DoIndependentWork();
This now executes on our primary thread, after the call has been queued (or possibly sent). Thus, this could be occuring while waiting for the request to be returned. Note that we are still on our primary thread.
string urlContents = await getStringTask;
At this point, our thread returns. .NET will create a continuation function containing the remainder of the method (return urlContents.Length;) which will automatically be called for us once the getStringTask completes on it's own thread. At that point, a new thread will pick up the continuation.
Through all of this, the entirety of what we wrote in our async method, up to the await keyword, ran in a single thread. Sure, we called another method that could happen to spawn another thread, but there was nothing that we did to create another thread just by using the async/await keywords. The continuation at the end can be called possibly by a different thread, but after our initial function has actually returned (Which is why our function returns a Task object, to represent work that may not have yet completed when any calls to this function returns).

Categories

Resources