Why this async method don't cause deadlock on a thread? - c#

I need an explanation why this code doesn't cause deadlock on the thread it's running in
(It's a WinForm application and these happens in button_Click):
Task FakeMainThread = Task.Run(async() => // fake main thread
{
async Task asyncMethod() // executed in FakeMainThread
{
await Task.Run(() => // 'await'ing in FakeMainThread
{
Thread.Sleep(5000);
MessageBox.Show("child Task finished");
return 3;
});
MessageBox.Show("asyncMethod finished");
}
asyncMethod().Wait(); // deadlock should appear in FakeMainThread
MessageBox.Show("FakeMainThread finished");
return 4;
});
asyncMethod().Wait(); should block the fakeMainThread Task's thread while it's await another task to finish and since await is happening in fakeMainThread too it shouldn't be able to 'await' and deadlock should appear on fakeMainThread. But it doesn't happen and I see bot MessageBox.Show("asyncMethod finished"); and MessageBox.Show("FakeMainThread finished"); messages.
Note: if I put this:
async Task asyncMethod()
{
await Task.Run(() =>
{
Thread.Sleep(5000);
MessageBox.Show("child Task finished");
return 3;
});
MessageBox.Show("asyncMethod finished");
}
asyncMethod().Wait(); // deadlock appears in Main Thread (UI)
MessageBox.Show("FakeMainThread finished");
in main button1_click() inside directli deadlock appears on UI. Thanks!

You don't experience a deadlock in the first code example because of the SynchronizationContext. This context says what the await must do to restore your code. When you start a new task (Task.Run), you will get the default context. In the button1_click you get the context from the form.
The context for the form allows to execute only a single thread (the one used to paint/update your form).
When you call .Wait(), then you keep that thread 'locked' with waiting until the task is done. That means that the thread is not released for another job and this is one of the reasons that the form goes into the 'not responding' state.
When you do an await [code], and that code is done, then it will ask the synchronization context to schedule the remainder of the code.
In case of the default context, any free thread is taken and your code will continue, the task is marked as done, and thus the 'loop' in the .Wait() gets signaled that the task is done and continues.
In case of the forms context, there is only a single thread, thus the completion of this task is scheduled to run after the current code is done. This is the reason of the deadlock.
Avoiding this deadlock is the main reason that you most often get the suggestion to use ConfigureAwait(false), this tells the await that it should substitute the current synchronization context with the default one, thus allowing you to use any free thread.
The only reason you don't want to use this (or say ConfigureAwait(true)) is if you need to update something on your form (WPF or WinForms) or you need your http context (ASP.NET, not ASP.NET Core). This is because only a single thread has access to your form or http context. Here you don't get an exception on your MessageBox.Show because this is 'outside' of the forms context and doesn't need this special thread/synchronization context.
See https://devblogs.microsoft.com/dotnet/configureawait-faq/ for more information.

Related

Await returns the control to caller- who executes synchronous code in the awaited task?

When await is encountered, control goes back to caller, while the awaited task runs (makes/waits for network request/response) in background. I know that awaiting task will not need thread when it is awaiting for network response as it is not really running anything but just waiting.
I want to ask - suppose in the awaited function, there is some synchronous code like Console.WriteLine, then because control has gone back to the caller, who executes the console.writeline?
Assume button click event does await Fn1();.
Example 1 :
async Task<string> fn1() {
string result = await NetworkCallAsync('http......');
return result;
}
In above example upon encountering await Fn1(), control goes back to the UI thread, while the network call is made. I understand this. When network call completes, the Ui thread will execute the rest of the code. That is - fetch the result and return.
Example 2:
async Task<string> fn1() {
Console.WriteLine("hello"); //or any other synchronous code like Thread.Sleep(10);
string result = await NetworkCallAsync('http......');
return result;
}
In above code, when the button click encounters the await Fn1() and has returned control to the UI thread, then who executes the Console.WriteLine (or Thread.Sleep(10)) as shown in above example)?
A task does not necessarily need any thread to execute. A task may represent an IO-operation, i.e. disk or network access. If so the task can use the asynchronous features in the OS to avoid using any thread at all while waiting.
If you are starting a task yourself you can specify a taskScheduler that determines what thread to use. Typically either the OS thread or an arbitrary threadpool thread.
It is also possible for tasks to complete synchronously, either because the result is already available, or because the method need to fulfill an API, and using real async IO is to much work.
As the user of a task you should in general not need to care how the task was run, just that it has completed.
The Console.WriteLine("hello"); will be invoked on the same thread where the Fn1() is invoked. The Fn1() will not return a Task<string> before executing all code that comes before the first await inside the Fn1. The Console.WriteLine("hello"); line is located before the first await, so it will run synchronously on the calling thread.

Await suspends the execution of the thread where it is used, Then how it will update the UI and improves performance

I know when we call an async method it frees the UI thread and suspends the execution of the thread when it encounters the await keyword.
Now suppose we are returning somevalue from the awaited method like:-
public async Task SomeEvent()
{
x= await Sum(2, 5); //Returns some output value
TextBox.Text = x; // updates the result in the UI
}
Now as per my understanding, since the UI thread returns back to the method which called this "SomeEvent" when it encounters the await keyword and It is necessary to perform all the UI related operations on the main thread.
Now in the above program, UI is busy in executing some other operations as it is freed from executing the above method and when asynchronously the execution of the awaited process completes then how the UI will get updated.
There is such thing as SynchronizationContext. Simply saying, the thread that completed some async work will wait (will be put into a queue) for the UI context to be free again, to run in it. It looks like this happens instantly, but actually it does not.

IO-bound async task not executes asynchronously

I has spend a lot of time to understand async programming principles. But one thing is still unclear. I was confused by this code :
static async Task Method()
{
Console.WriteLine($"Method entered.");
await Task.Delay(1000);
Console.WriteLine($"Await 1 finished.");
await Task.Delay(1000);
Console.WriteLine($"Await 2 finished");
}
static int Main(string[] args)
{
Console.WriteLine($"Main started.");
return AsyncContext.Run(() => MainAsync(args));
}
static async Task<int> MainAsync(string[] args)
{
var t = Method();
Console.WriteLine("Thread starting sleep.");
Thread.Sleep(10000);
Console.WriteLine("Thread stopped sleeping");
Console.WriteLine(t.IsCompleted ? "Method completed" : "Method not completed");
await t;
return 0;
}
Result :
Main started.
Method entered.
Thread starting sleep.
Thread stopped sleeping
Method not completed
Await 1 finished.
Await 2 finished
As I understand while Main thread is sleeping IO-bound operations from Method should be executed (cause Task.Delay emulate IO) and interrupt Main thread sequentially to continue execute Method code.
So I expect to see:
Main started.
Method entered.
Thread starting sleep.
Await 1 finished.
Await 2 finished
Thread stopped sleeping
Method completed
I know that by Thread.Sleep I am making to stop Main thread. But as I understand Method() should not need thread because it consists of IO-bound operations.
Can anybody explain where I am misunderstanding it?
AsynContext that I am using is (here).
By default "await" captures the current synchronization context and spawns the continuation in that original context.
If the context is undefined, continuation is spawned in the default thread pool (TaskScheduler.Default).
I'm not familiar with AsyncContext, but it probably spawns MainAsync under some well synchronization context and since Thread.Sleep blocks the thread that occupies that context, the continuation of "await" will wait until the context is freed.
This is not a strange phenomenon, you can reproduce it without the AsyncContext class. Try to run the same code in a Windows forms application and you will see.
Windows forms have it's own synchronization context that guards against unsynchronized Control manipulation.
To overcome this, you could tell "await" to not capture the synchronization context by using the ConfigureAwait(false) method.
static async Task Method()
{
Console.WriteLine($"Method entered.");
await Task.Delay(1000).ConfigureAwait(false);
Console.WriteLine($"Await 1 finished.");
await Task.Delay(1000).ConfigureAwait(false);
Console.WriteLine($"Await 2 finished");
}
await won't try to spawn continuation in the existing context, rather it would spawn it in a thread pool task.
Why your code is behaving correctly as expected ?
On using the AsyncContext.Run, you are providing an explicit context for Console application which otherwise have NULL Synchronization Context, now when you execute the following lines of code in MainAsync:
var t = Method();
Console.WriteLine("Thread starting sleep.");
Thread.Sleep(10000);
Then Method() starts executing, where on encountering the statement:
await Task.Delay(1000);
It cedes the control back to the caller, where you block the context by making the Thread Sleep for 10s Thread.Sleep(10000);, so now before this sleep is over the Continuation cannot take place in the Async method, since it waits for the Continuation context to be available, moment its free, then it starts executing the Continuation, but by that time it also complete the remaining statements in the MainAsync, which seems to be prioritized and response is as expected, it awaits only in the very end, in fact checking the Task status for any logic like t.IsCompleted is more of a code smell, better is only await t, which waits for the Task completion
There are two ways to get the behavior that you expect
As shown by #Arik, configure both await using ConfigureAwait(false), what does this means, simple that for running the Async continuation it doesn't need the original context and that will continue as true Async operation, hence will provide the result as you expect. Most of the libraries wit Async functions especially IO based, implement ConfigureAwait(false).
Make the call from the Main as return MainAsync(args).Result;, this will ensure standard behavior of the Console applications, which means NULL Synchronization Context, which means Async doesn't care about continuation on any existing Context, it goes on in background even when you are making the Thread sleep, since it doesn't expect that context and the result would be same as you expect
Main started.
Method entered.
Thread starting sleep.
Await 1 finished.
Await 2 finished
Thread stopped sleeping
Method completed
0
AsyncContext schedules all tasks to be executed on a single thread. Your Method consists of Delays and WriteLines. You may think of Delays as analogous to IO operations in that they do not need a thread to be executed on. However, WriteLine requires a thread. Thus, when Method is awaken from Delay it waits for thread be available to execute WriteLine.
Actually the Method would block even if it does not contain WriteLines but only Delays because it needs a thread for the Delay to return to and to start new Delay, but that would be more difficult to notice without WriteLines.

Is it safe to change ui properties after await in async methods?

I was messing up with c#'s await/async when I wrote the following program:
using System;
using System.Threading;
using System.Threading.Tasks;
class Program
{
static void Main(string[] args)
{
Test();
while (true) { }
}
static async void Test()
{
var t = Task.Run(()=> Thread.Sleep(1000));
await t;
throw new Exception("This exception happens in a worker thread");
}
}
When I run this program, I can clearly see in Visual Studio's thread window that the exception happens in a Worker Thread, rather than the Main Thread, this leads me to believe that when I await a task, my method can be finished by another thread.
However, when I open a new windows forms application and add the following event handler:
async void button1_Click(object sender, EventArgs e)
{
var task = Task.Run(() => Thread.Sleep(1000));
await task;
button1.Text = "I'm in the main thread!"; //this works
}
This works, so I notice that in a console application my Test function is resumed by a worker thread, while in a Windows Forms application, it's always resumed by the main thread.
Why is this? Is it reliable?
Yes. It is reliable.
When you do await, the current SynchronizationContext is captured so that when it continues after the await, this context will be used to execute the code.
In GUI applications, the current SynchronizationContext is a context that will execute code on the UI thread.
In Console Applications, the current SynchronizationContext is null, therefore the Task continues on a thread-pool thread.
Take a look at this blog post.
As I explain in my async intro, by default await will capture a "context" and resume execution of its async method in that context.
Technically, this context is SynchronizationContext.Current unless it is null, in which case it is TaskScheduler.Current.
In everyday terms, this means that the context is a UI context if the method is running on a UI thread; it is an ASP.NET request context if the method is servicing an ASP.NET request; and it is most likely the thread pool context in all other situations.
Note that this is default behavior. You can specify that the method does not need to resume on its context by awaiting the result of ConfigureAwait(continueOnCapturedContext: false). In this case, it is likely that the rest of the method will be executed on a thread pool thread; but technically this just means that the method will be executed "somewhere" and you don't care where.
Yes, this is correct and one of the intended uses of async / await.
The Task that you are awaiting is what happens asynchronously. When it resumes, it resumes on the calling thread, which will be the UI thread in the case of a WinForms event handler.
Note that it is possible for you to change this behavior using Task.ConfigureAwait(False). When the task is configured this way, it will not pass control back to the original thread and will instead resume on the task's thread.
All of this behavior is dependant on the application's current SynchonizationContext though. Some application types will have different contexts which can subtly change the behaviors. Console Applications are a good example of this, as they do not set a synchronization context by default, and will resume on the Task's thread. You can change this if you want by creating a context for the console app, as shown here.

If an async method is single threaded how can it be run in the background?

I'm trying to understand async/await and have read a number of articles but am still confused about the synchronous/asynchronous nature.
I have the following test console app:
static void Main(string[] args)
{
var test = FooAsync();
Console.WriteLine("After FooAsync");
for (int i = 0; i < 100; i++)
Console.WriteLine("After that");
Console.ReadKey();
}
private static async Task FooAsync()
{
Console.WriteLine("Before delay");
await Task.Delay(1);
Console.WriteLine("After delay");
}
The code gives output along the lines of:
Before delay
After FooAsync
After that
After that
After that
After that
After delay
After that
.
.
I understand that async/await will not create a separate thread for processing and that at the point FooAsync reaches the await Task.Delay(1) line it will return back to Main as the task will not yet have completed, however, as we are only running on a single thread can someone explain what triggers the FooAsync method to resume at some arbitrary point within Main before Main can then continue?
Update
I take it back and i3arnon and dariogriffo are correct. The code does use multiple threads (as I'd have seen before had looked in the debugger or done the obvious as kha suggested). I'd been confused by the Threads section on the following page https://msdn.microsoft.com/en-us/library/hh191443.aspx#BKMK_Threads not realising that a "continuation" actually refers to a continuation task schedule to run as soon as the task being "awaited" finishes.
This isn't single threaded.
When the delay task completes the rest of the method is posted to the ThreadPool and runs concurrently with your main thread. The "trigger" here is the callback of the internal System.Threading.Timer being used inside Task.Delay.
This behaviour depends on the SynchronizationContext. In a UI environment this would have been posted to the same UI thread and would have to wait until that thread is free.
If you would have been waiting for the task returned from FooAsync then you would only have a single thread running each time.
Async/await may create new threads OR NOT, it depends of the nature of the operation.
If the operation is an IO (for example disks/network operations) probably is coded in a way it will not spin a new thread. You can read from here:
The async and await keywords don't cause additional threads to be created?
If you create your own Async operation and you create a thread, that's a different story, that's why you shouldn't do async over sync
http://blogs.msdn.com/b/pfxteam/archive/2012/04/13/10293638.aspx
You can check this also but using Thread.CurrentThread to get the Id of the process. (Add that to a Console.WriteLine)
It's a pretty common misconception that the async or await keywords create new threads. They don't.
The threads are created by running a Task. In this case, the thread is created by the Task.Delay call.

Categories

Resources