ConfigureAwait(false) doesn't make the continuation code run in another thread - c#

I have a code which runs on thread1.
I call a function in a synchronic way (using async method but it shouldn't disturb me - async method doesn't turn code to be asynchronic).
I have an await with ConfigureAwait set to false, so I understood the
code after it is a task continuation which suppose to run in a different thread than the code before the await (because ConfigureAwait was set to false).
By my test - All code run in the same thread.
How is it? Why doesn't the code, below the await, run on a different thread?
This is the code:
public async void F1()
{
Console.WriteLine($"Thread.CurrentThread.ManagedThreadId={Thread.CurrentThread.ManagedThreadId}");
int x = await F2().ConfigureAwait(false);
Console.WriteLine($"Thread.CurrentThread.ManagedThreadId={Thread.CurrentThread.ManagedThreadId}");
}
private async Task<int> F2()
{
Console.WriteLine("Begins F2");
Console.WriteLine($"Thread.CurrentThread.ManagedThreadId={Thread.CurrentThread.ManagedThreadId}");
Console.WriteLine("Finishes F2");
return 7;
}
This is the output:
Thread.CurrentThread.ManagedThreadId=1
Begins F2
Thread.CurrentThread.ManagedThreadId=1
Finishes F2
Thread.CurrentThread.ManagedThreadId=1

It is not "supposed to run in a different thread context", it is allowed to. In the opposite case (ConfigureAwait(true)) it must continue on the same thread context.
Furthermore when there is nothing to await (inside the method), the "async" method runs synchronously so doesn't need to return to some thread context, it is still on it.

I have an await with ConfigureAwait set to false, so I understood the code after it is a task continuation which suppose to run in a different thread than the code before the await (because ConfigureAwait was set to false).
No. There are a couple of misunderstandings here.
The first misunderstanding is regarding what ConfigureAwait(false) does.
ConfigureAwait (and await) have nothing to do with threading directly. By default, await captures a context - the current SynchronizationContext or TaskScheduler. This context could resume on the same thread (e.g., UI SynchronizationContexts commonly do this), but "context" does not necessarily mean "thread" (e.g., the ASP.NET pre-Core SynchronizationContext can resume on any thread pool thread).
What ConfigureAwait(false) actually does is skip capturing that context. So the thread pool context is used, which may run on any thread pool thread. Note that if the code before await was running on a thread pool thread, it may resume on any thread pool thread, including the thread that it was running on before.
The second misunderstanding is regarding when ConfigureAwait(false) is applied.
await will first check to see if its awaitable is complete, and only then will it actually behave asynchronously. So if you await an already-completed task, the ConfigureAwait(false) is never even considered - the code just continues running synchronously.
How can I enforce it to run on a different thread then?
Use Task.Run. Task.Run is the proper tool to use when you need to run code on a thread pool thread.

Related

What is the behavior of async method called from sync context for the code snippet shown?

In following code snippet, I am accessing async method in sync context. The parent thread is running in thread pool.
public async Task AsyncWriteToEventHub(Data data, string correlationId)
{
await this.eventHubClinet.SendBatchAsync(data);
}
public void WriteData(Data data)
{
AsyncWriteToEventHub(Data);
}
Surprisingly, I am not getting any compilation error while calling AsyncWriteToEventHub() event though I am not using await() or wait() in WriteData.
Questions:
Will there be a deadlock since I am running these functions in thread pool?
Does AsyncWriteToEventHub will run on separate thread? If Yes, I am not waiting on the thread object?
What will happen to child thread when parent completes the execution? AsyncWriteToEventHub is a fire-and-forget method for me.
Will there be a deadlock since I am running these functions in thread pool?
There is no deadlock. Your code is not blocking on asynchronous code, so the classic deadlock (as described on my blog) will not occur.
Does AsyncWriteToEventHub will run on separate thread?
No. Why would it?
If Yes, I am not waiting on the thread object? What will happen when parent completes the execution?
Your code is not (a)waiting for the method to complete. So your code never knows when the method completes. So your code cannot know when it is safe for the application to exit, and it cannot even be aware of exceptions thrown by the asynchronous method.
Usually, this is not desirable. It's more natural to await every task sooner or later.

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.

What happens when awaiting a call on a different thread

Let's say I invoke a Task like this:
Task.Factory.StartNew(async () =>
{
await SomeAction();
}, TaskCreationOptions.LongRunning);
and inside SomeAction there's a line like this:
await OtherAction();
with a regular await, the code goes back to the invoking context, but here it's a task running on a different thread. So what happens with the context of the await there? Is it actually like blocking in this case?
with a regular await, the code goes back to the invoking context, but here it's a task running on a different thread. So what happens with the context of the await there? Is it actually like blocking in this case?
Here's how await works. await never blocks.
await will first examine its awaitable (usually a task); if the awaitable is already complete, then await will extract the result (or exception) and just continue executing the method (synchronously). If the awaitable is not already complete, then await will behave asynchronously.
When await behaves asynchronously, it will (by default) capture the current "context" (SynchronizationContext.Current or, if that is null, then TaskScheduler.Current), and then return an incomplete task. Since the example code is using Task.Factory.StartNew without specifying a TaskScheduler (which IMO is always a bug), then its delegate was executed on whatever TaskScheduler.Current was at the point StartNew was called. So the context that will be captured by the await is that same TaskScheduler.Current.
If TaskScheduler.Current was the same as TaskScheduler.Default (which is likely but by no means guaranteed), then when the async method returns, the thread is returned to the thread pool, and since StartNew doesn't understand async lambdas, the Task returned from StartNew is completed. This means the LongRunning flag in there just makes things slower; it's the opposite of optimization.
Later, when the await completes, it will resume executing the lambda on the captured context (TaskScheduler.Current). If TaskScheduler.Current was the same as TaskScheduler.Default, then the lambda will resume executing on a thread newly acquired from the thread pool (possibly the same thread but likely a different one).
This sounds crazy complicated, but really that's only because the code is using StartNew. If you look at the code using Task.Run, it's much simpler because the context is always TaskScheduler.Default (the thread pool context):
Task.Run(async () =>
{
await SomeAction();
});
Now you can say with certainty that if the await acts asynchronously, then it will return a task, returning the thread to the thread pool. When SomeAction completes, then the lambda will continue executing on a thread pool thread. Also, since Task.Run understands async lambdas, the task returned from Task.Run will not complete until the lambda is complete.
Here's some guidelines:
NEVER, EVER, EVER use Task.Factory.StartNew without passing a TaskScheduler. This is a 100%-of-the-time, always-on rule.
For asynchronous code, use Task.Run instead of Task.Factory.StartNew.

Can this "Synchronous" Code Cause Deadlocks?

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.

Is Async/Await using Task.Run starting a new thread asynchronously?

I have read a lot of articles and still cant get understand this part.
Consider this code :
private async void button1_Click(object sender, EventArgs e)
{
await Dosomething();
}
private async Task<string> Dosomething()
{
await Task.Run((() => "Do Work"));
return "I am done";
}
First question:
When I click the button, it will Call DoSomething and await a Task that creates a Thread from the threadpool by calling Task.Run ( if I am not mistaken ) and all of this runs asynchronously. So I achieved creating a thread that does my work but doing it asynchronously? But consider that I don't need any result back, i just want the work to be done without getting any result back, is there really a need to use async/await , and if so, how?
Second question:
When running a thread asynchronously, how does that work? Is it running on the main UI but on a separate thread or is it running on a separate thread and separate is asynchronously inside that method?
The purpose of creating Async methods is so you can Await them later. Kind of like "I'm going to put this water on to boil, finish prepping the rest of my soup ingredients, and then come back to the pot and wait for the water to finish boiling so I can make dinner." You start the water boiling, which it does asynchronously while you do other things, but eventually you have to stop and wait for it. If what you want is to "fire-and-forget" then Async and Await are not necessary.
Simplest way to do a fire and forget method in C#?
Starting a new task queues that task for execution on a threadpool thread. Threads execute in the context of the process (eg. the executable that runs your application). If this is a web application running under IIS, then that thread is created in the context of the IIS worker process. That thread executes separately from the main execution thread, so it goes off and does its thing regardless of what your main execution thread is doing, and at the same time, your main execution thread moves on with its own work.
1
There's a big difference if you don't await the Task or you await it:
Case you don't await it: DoSomething is called but next sentence is executed while DoSomething Task hasn't been completed.
Case you await it: DoSomething is called and next sentence is executed once DoSomething Task has been completed.
So, the need of async/await will depend on how you want to call DoSomething: if you don't await it is like calling it the fire & forget way.
2
Is it running on the main UI but on a separate thread or is it running
on a seperate thread and separate is asynchronously inside that
method?
Asynchronous code sometimes means other thread (see this Q&A Asynchronous vs Multithreading - Is there a difference?). That is, either if the code is being executed in a separate thread from the UI one or it lets continue the processing of the UI thread while it gets resumed, it's nice because UI loop can still update the screen while other tasks are being done in parallel without freezing the UI.
An asynchronous method (i.e. async method) is a syntactic sugar to tell the compiler that await statements should be treated as a state machine. The C# compiler turns your async/await code into a state machine where code awaiting a Task result is executed after the code that's being awaited.
Interesting Q&As
You might want to review these other Q&As:
Async/Await vs Threads
What's the difference between Task.Start/Wait and Async/Await?
async/await - when to return a Task vs void?
Is Async await keyword equivalent to a ContinueWith lambda?
OP said...
[...] But does this mean that "async/await" will fire off a thread and
Task.Run also fires off a thread or are they both the same thread?
Using async-await doesn't mean "I create a thread". It's just a syntactic sugar to implement continuations in an elegant way. A Task may or may not be a thread. For example, Task.FromResult(true) creates a fake task to be able to implement an async method without requirement it to create a thread:
public Task<bool> SomeAsync()
{
// This way, this method either decides if its code is asynchronous or
// synchronous, but the caller can await it anyway!
return Task.FromResult(true);
}
The type Task<TResult> requires you to return a TResult from your task. If you don't have anything to return, you can use Task instead (which, incidentally, is the base class of Task<TResult>).
But keep in mind that a task is not a thread. A task is a job to be done, while a thread is a worker. As your program runs, jobs and workers become available and unavailable. Behind the scenes, the library will assign your jobs to available workers and, because creating new workers is a costly operation, it will typically prefer to reuse the existing ones, through a thread pool.

Categories

Resources