Async and await - difference between console, Windows Forms and ASP.NET
Since, in UI thread based application, the UI thread is always available (unless the process is stopped explicitly or by Windows), so the ThreadPool thread responsible for executing the code after "await" in any async method, will guarantee to find the UI thread to post the results back (if any).
This is slightly confused. There's no indication that a ThreadPool
thread will be required at all.
It's up to the awaitable implementation to determine where to run the continuation, but normally the current synchronization context is used to work out where to run it:
- In a console application, there is no synchronization context, so a thread pool thread is used for the continuation.
- In a Windows Forms application, when you're running on the UI thread the synchronization context is one which will execute code on the UI thread... so the continuation executes there (unless you use
ConfigureAwait
, etc...) - In an ASP.NET application, there's a synchronization context specific to ASP.NET itself.
See Stephen Cleary's MSDN article for more details.
It's not clear what you mean by your later question about having to call Wait
or Result
in ASP.NET or a console app... in both scenarios it may be required, but equally it may not be. It depends on what you're doing really. Don't forget that even a console app can start its own threads and do all kinds of other things - you can write an HTTP server in a console app, for example...
Use of async/await in console or web apps
The point with async-await is that the calling thread is always freed up when you reach the first asynchronous point (i.e. the first await
of an uncompleted task).
In UI apps you have a SynchronizationContext
that posts the code after the awaits to the UI thread because code that interacts with the UI must be executed by the UI thread otherwise you'll get an exception. You can control that by using ConfigureAwait(false)
.
In console apps (and services, etc.) there's no such need, so that code runs on some ThreadPool
thread. The calling thread (which is likely to be also a ThreadPool
thread) was freed up and was able to do other kinds of work in the meantime instead of blocking synchronously. So async-await improves scalability as it enables doing more work with the same amount of threads.
Utilizing Async/Await in .NET Console applications breaks when calling Application.Run() or instantiating a WinForms UserControl object
In the absence of synchronization context (or when the default SyncrhonizationContext
is used), it's often possible for an await
continuation to run synchronously, i.e., on the same thread where its antecedent task has ended. That can lead to obscure deadlocks, and it was one of the reasons TaskContinuationOptions.RunContinuationsAsynchronously
was introduced in .NET Framework 4.6. For some more details and examples, check out this blog post: The danger of TaskCompletionSource class.
The fact that AsyncPump
stops your code from hanging indicates you may have a similar situation somewhere inside mcc.Run()
. As AsyncPump
imposes true asynchrony for await
continuations (albeit on the same thread), it reduces the chance for deadlocks.
That said, I'm not suggesting using AsyncPump
or WindowsFormsSynchronizationContext
as a workaround. Rather, you should try to find what exactly causes your code to hang (and where), and solve it locally, e.g. simply by wrapping the offending call with Task.Run
.
One other issue I can spot in your code is that you don't wait or await the task returned by MainAsync
. Because of that, at least for the console branch of your logic (especially without using AsyncPump
), your program may be ending prematurely, depending on what's going in inside mcc.Run()
, and you may be letting some exceptions go unobserved.
async and await in an asp.net core mvc web project
It frees up the cpu to service other requests to your controllers rather than busy wait on whatever async operations you are doing. This answer has a pretty good analogy to help explain:
Think of it like getting on a bus, there's five people waiting to get on, the first gets on, pays and sits down (the driver serviced their request), you get on (the driver is servicing your request) but you can't find your money; as you fumble in your pockets the driver gives up on you and gets the next two people on (servicing their requests), when you find your money the driver starts dealing with you again (completing your request) - the fifth person has to wait until you are done but the third and fourth people got served while you were half way through getting served. This means that the driver is the one and only thread from the pool and the passengers are the requests.
Without an async controller, the passengers behind you would have to wait ages while you looked for your money, meanwhile the bus driver would be doing no work.
Thread control flow in async .NET Console program
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'sMain
method is invoked,SynchronizationContext.Current
will returnnull
. 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 ║
╚═════════╩═══════════╩════════════╩════════════╩══════════╩══════════╝
await Task.Run(...) behaving differently for console and windows application
That's something that happens quite often. You're causing a deadlock.
To put it simply, frameworks like WinForms and WPF "enforce" a thread synchronization context, which means that whenever you launch a new task, the rest of your code will continue on the same thread as it started. This is done to ensure that, for example, code that started on the UI thread will continue running on the same thread after the task returns.
Because you're blocking on the task (with the Wait
method), and the task is trying to return a value to that blocking thread, both threads go into a deadlock state.
This behaviour doesn't happen in a console application because no thread synchronization context is enforced, the continuation of a task can run on a completely different third thread.
Stephen Cleary explains this very well here: http://blog.stephencleary.com/2012/07/dont-block-on-async-code.html
Why do .NET console programs not deadlock the way UI programs do?
There are two parts to the classic deadlock:
- Blocking on asynchronous code (that does not use
ConfigureAwait(false)
everywhere). - An asynchronous context that only permits one thread in at a time.
Your example code always does (1) in both environments. The difference is in (2). Specifically, UI apps have a SynchronizationContext
for their UI thread so that await
will naturally return to the UI thread after the await
completes. Console apps do not have that context, so the deadlock does not happen.
Related Topics
Datagridview: How to Set a Cell in Editing Mode
How to Embed Multiple Images in Email Body Using .Net
Use Dependency Injection in Static Class
Group by Variable Integer Range Using Linq
C# Arrow Key Input for a Console App
Regex Split String But Keep Separators
Wix Silent Install Unable to Launch Built in .Exe: Wix V3
How to Use Push Notifications in Xamarin Forms
Using a 32Bit or 64Bit Dll in C# Dllimport
How to Mix Colors "Naturally" with C#
How to Force My C# Winforms Program Run as Administrator on Any Computer
How to Resize Image in C# Winrt/Winmd
A Dictionary Object That Uses Ranges of Values for Keys
Passing Array of Strings to Webmethod with Variable Number of Arguments Using Jquery Ajax
Play Multiple Sounds Using Soundplayer
In-Use Files Not Updated by Msi-Installer (Visual Studio Installer Project)