Async lesson 2 of N: async void probably isn’t what you want

In the previous post you might have noticed that we had a Console.ReadLine near the end of Main – while it’s relatively common for demo code to do so, in this case it was actually necessary for the program to work.

Why’s that?  Well, let’s remove it and see what we get for output:

And here’s what we get when we run it:

…\bin\Debug » .\ItsBigItsHeavyItsWood.exe
Starting to download url
http://www.google.com/
Starting to download url http://www.microsoft.com/
Starting to download url http://www.apple.com/
…\bin\Debug »

Wait, what happened?  We started the 3 downloads just fine, but then nothing else happened?  Why’s that?

SImply put ‘async void’ means ‘fire and forget’, and that means *really* forget.  You don’t know when (or even if) the item completes.

In this case, when the last DownloadUrl call happens, it hits the await, returns to Main, and Main has nothing left to do, so it returns.  Since that’s the entry point of the app, the process exits.

The fact that 3 tasks are still running doesn’t make any difference, Main returned.

If we want to care about whether something completes (and more specifically, knowing when it completes), then we have to tell the compiler that by changing the return type to ‘async Task’.

This is probably the ‘weirdest’ form of async method, since you end with a method that has no ‘return’ in it (not even a ‘yield return’) yet the method itself actually returns something (the Task).

Since we also need to do something to make sure these 3 tasks complete before we return from Main, we need to do something else.

A first reaction might be to ‘await DownloadUrl(url)’, but while that would both throw away our parallelism (since it would block the start of a task until the previous one completed), it also isn’t possible here since it would mean the containing method (Main) would need to be marked async, and you can’t do that with entry point methods, as the compiler lets you know:

error CS4009: ‘Foo.Program.Main()': an entry point cannot be marked with the ‘async’ modifier

This means we’re back to using ‘normal’ TPL methods, which in this case means waiting on the tasks to complete. Since we want them all to complete, we need to Task.WaitAll which takes an array of tasks.  We could create our array of tasks to wait on in a variety of manners (foreach loop, etc.).

Now that we’re waiting on the tasks to complete, we get what we expected – they all complete and we get the expected output:

…\bin\Debug » .\ItsBigItsHeavyItsWood.exe
Starting to download url
http://www.google.com/
Starting to download url http://www.microsoft.com/
Starting to download url http://www.apple.com/
Downloaded 16694 bytes
Downloaded 34329 bytes
Downloaded 1020 bytes
…\bin\Debug »

About these ads