I am new to asynchronous programming with the async
modifier. I am trying to figure out how to make sure that my Main
method of a console application actually runs asynchronously.
class Program { static void Main(string[] args) { Bootstrapper bs = new Bootstrapper(); var list = bs.GetList(); } } public class Bootstrapper { public async Task<List<TvChannel>> GetList() { GetPrograms pro = new GetPrograms(); return await pro.DownloadTvChannels(); } }
I know this is not running asynchronously from "the top." Since it is not possible to specify the async
modifier on the Main
method, how can I run code within main
asynchronously?
An easy fix is to move the async code block into a separate method that you can mark with async and call from Main : class Program { static void Main(string[] args) { // Call SomeAsyncCode() somehow } private static async Task SomeAsyncCode() { // Use await here! await Task. Delay(10); // Other async goodness... } }
From C# 7.1, it is also possible to define the Main method as async with any of the following additional overloads. static Task Main(); static Task < int > Main(); static Task Main(string[] args); static Task < int > Main(string[] args); An async Main method enables you to use await in your Main method.
async and await Inside an async function, you can use the await keyword before a call to a function that returns a promise. This makes the code wait at that point until the promise is settled, at which point the fulfilled value of the promise is treated as a return value, or the rejected value is thrown.
C# has a language-level asynchronous programming model, which allows for easily writing asynchronous code without having to juggle callbacks or conform to a library that supports asynchrony. It follows what is known as the Task-based Asynchronous Pattern (TAP).
As you discovered, in VS11 the compiler will disallow an async Main
method. This was allowed (but never recommended) in VS2010 with the Async CTP.
I have recent blog posts about async/await and asynchronous console programs in particular. Here's some background info from the intro post:
If "await" sees that the awaitable has not completed, then it acts asynchronously. It tells the awaitable to run the remainder of the method when it completes, and then returns from the async method. Await will also capture the current context when it passes the remainder of the method to the awaitable.
Later on, when the awaitable completes, it will execute the remainder of the async method (within the captured context).
Here's why this is a problem in Console programs with an async Main
:
Remember from our intro post that an async method will return to its caller before it is complete. This works perfectly in UI applications (the method just returns to the UI event loop) and ASP.NET applications (the method returns off the thread but keeps the request alive). It doesn't work out so well for Console programs: Main returns to the OS - so your program exits.
One solution is to provide your own context - a "main loop" for your console program that is async-compatible.
If you have a machine with the Async CTP, you can use GeneralThreadAffineContext
from My Documents\Microsoft Visual Studio Async CTP\Samples(C# Testing) Unit Testing\AsyncTestUtilities. Alternatively, you can use AsyncContext
from my Nito.AsyncEx NuGet package.
Here's an example using AsyncContext
; GeneralThreadAffineContext
has almost identical usage:
using Nito.AsyncEx; class Program { static void Main(string[] args) { AsyncContext.Run(() => MainAsync(args)); } static async void MainAsync(string[] args) { Bootstrapper bs = new Bootstrapper(); var list = await bs.GetList(); } }
Alternatively, you can just block the main Console thread until your asynchronous work has completed:
class Program { static void Main(string[] args) { MainAsync(args).GetAwaiter().GetResult(); } static async Task MainAsync(string[] args) { Bootstrapper bs = new Bootstrapper(); var list = await bs.GetList(); } }
Note the use of GetAwaiter().GetResult()
; this avoids the AggregateException
wrapping that happens if you use Wait()
or Result
.
Update, 2017-11-30: As of Visual Studio 2017 Update 3 (15.3), the language now supports an async Main
- as long as it returns Task
or Task<T>
. So you can now do this:
class Program { static async Task Main(string[] args) { Bootstrapper bs = new Bootstrapper(); var list = await bs.GetList(); } }
The semantics appear to be the same as the GetAwaiter().GetResult()
style of blocking the main thread. However, there's no language spec for C# 7.1 yet, so this is only an assumption.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With