Freigeben über


Simple consoles and async

[UPDATE: 12/11/2017 - Asynchronous Main methods are now directly supported in c# 7.1. See this post https://blogs.msdn.microsoft.com/benwilli/2017/12/08/async-main-is-available-but-hidden/]

When presenting, I like to demonstrate simple concepts in console apps because they don’t have any other baggage around them. This is a problem with async code though because you cannot have an async Main method. Entry point methods cannot be marked async.

To do async work here, though, you don’t have to explicitly start another thread, you just need to move the async call down a level in the callstack and then wait on the resulting task to complete (so the app doesn’t exit) like this.

 static void Main(string[] args)
{
    Task t = MainAsync(args);
    t.Wait();
}

static async Task MainAsync(string[] args)
{
Console.WriteLine("Doing Thing 1");
await Task.Delay(5000);

Console.WriteLine("Doing Thing 2");
await Task.Delay(5000);

Console.WriteLine("Doing Thing 3");
await Task.Delay(5000);
}

I could leave well enough alone here, but for demo purposes this doesn’t really show that anything is happening asynchronously visually in the console window. It just shows the output in order as we would expect. But since this Task stuff is happening we can do something interesting. Instead of just waiting until the task is done, lets put up some sort of busy indicator.

 static void Main(string[] args)
{
    ShowProgressIndicatorUntil(MainAsync(args));
}

static async Task MainAsync(string[] args)
{
Console.WriteLine("Doing Thing 1");
await Task.Delay(5000);

Console.WriteLine("Doing Thing 2");
await Task.Delay(5000);

Console.WriteLine("Doing Thing 3");
await Task.Delay(5000);
}

static void ShowProgressIndicatorUntil(Task task)
{
foreach (char c in BusyChars())
{
if (task.IsCompleted)
{
break;
}
Console.Write(c);
Console.CursorLeft = 0;
Task.Delay(100).Wait();
}
}

static IEnumerable<char> BusyChars()
{
while (true)
{
yield return '\\';
yield return '|';
yield return '/';
yield return '-';
}
}

 

Now, instead of calling the MainAsync directly, I’ve passed it into another method which will just loop until the IsCompleted property is true while updating the console with a spinning bar. (If we didn’t break, the loop would never end because the BusyChars() creates an infinite series of characters)

Never leave your user staring at a static screen while you do some work that he is waiting on. Otherwise he will be sorely tempted to hit the X in the upper right corner.