This question already has an answer here:
-
Async and await - difference between console, Windows Forms and ASP.NET
1 answer
I was messing around with async/await in C# just to dig into some of the thread control flow and stumbled upon an unusual behavior that I would really appreciate clarification on.
It would make sense that the execution after await continues on a calling thread even if the Task itself was executed in background. And in fact that's exactly what happens with, let's say, WPF.
The following code:
private async void Button_Click(object sender, RoutedEventArgs e)
{
Console.WriteLine($"Start. Thread: {Thread.CurrentThread.ManagedThreadId}");
await Task.Run(async () => await Task.Delay(1000));
Console.WriteLine($"End. Thread: {Thread.CurrentThread.ManagedThreadId}");
}
Results in:
Start. Thread: 1
End. Thread: 1
I realize that that's the way to make the program flow predictable etc.
But what surprised me is that the async Main method feature of .NET Console applications shows somewhat different behavior.
The same code:
static async Task Main(string[] args)
{
Console.WriteLine($"Start. Thread: {Thread.CurrentThread.ManagedThreadId}");
await Task.Run(async () => await Task.Delay(1000));
Console.WriteLine($"End. Thread: {Thread.CurrentThread.ManagedThreadId}");
}
Results in a different thread control flow:
Start. Thread: 1
End. Thread: 5
My guess is that the console application has a different concept of a synchronization context and are not bound to a main "UI" thread unlike WPF. But i'm actually struggling to find some clear info apropos of this.
In short, When the SynchronizationContext.Current
not is set, (which is the case on a console application). The await response is invoked on the ThreadPool
.
On a Winforms/WPF a SynchronizationContext is implemented to queue the response to either the winforms controlToSendTo.BeginInvoke();
or the WPF Dispatcher.BeginInvoke();
.
Reference:
Await, SynchronizationContext, and Console Apps (a blog post by a member of the dev team):
But there's one common kind of application that doesn't have a SynchronizationContext
: console apps. When your console application's Main
method is invoked, SynchronizationContext.Current
will return null
. That means that if you invoke an asynchronous method in your console app, unless you do something special, your asynchronous methods will not have thread affinity: the continuations within those asynchronous methods could end up running "anywhere."
Parallel Computing - It's All About the SynchronizationContext (an article referenced from the official documentation for the SynchronizationContext
class):
By default, all threads in console applications and Windows Services only have the default SynchronizationContext
.
...
Figure 4 Summary of SynchronizationContext
Implementations
...
╔═════════╦═══════════╦════════════╦════════════╦══════════╦══════════╗
║ ║ Specific ║ Exclusive ║ Ordered ║ Send May ║ Post May ║
║ ║ Thread ║ (Delegates ║ (Delegates ║ Invoke ║ Invoke ║
║ ║ Used to ║ Execute ║ Execute ║ Delegate ║ Delegate ║
║ ║ Execute ║ One at ║ in Queue ║ Directly ║ Directly ║
║ ║ Delegates ║ a Time) ║ Order) ║ ║ ║
╠═════════╬═══════════╬════════════╬════════════╬══════════╬══════════╣
║ ... ║ ║ ║ ║ ║ ║
╠═════════╬═══════════╬════════════╬════════════╬══════════╬══════════╣
║ Default ║ No ║ No ║ No ║ Always ║ Never ║
╚═════════╩═══════════╩════════════╩════════════╩══════════╩══════════╝