Home > front end >  Why does Main not wait for all threads to evaluate in asynchronous program?
Why does Main not wait for all threads to evaluate in asynchronous program?

Time:05-21

@Dan Dinu's answer from a previous question regarding asynchronous programming in C# provides a useful minimal example, which I have adapted as follows:

// From https://stackoverflow.com/questions/14455293/how-and-when-to-use-async-and-await

using System;
using System.Threading.Tasks;

namespace minimal_async_await_SE
{
    internal class Program
    {
        public static async Task MyMethodAsync()
        {
            Task<int> longRunningTask = LongRunningOperationAsync();
            // independent work which doesn't need the result of LongRunningOperationAsync
            // can be done here
            Console.WriteLine("Independent work");
            //Call await on the task
            int result = await longRunningTask;
            Console.WriteLine(result);
        }

        public static async Task<int> LongRunningOperationAsync()
        {
            await Task.Delay(1000);
            return 1;

        }

        static void Main(string[] args)
        {
            MyMethodAsync();
            Console.WriteLine("Returned to Main");
            //Console.ReadKey();
        }
    }
}

If I uncomment line 32, I get the following expected result:

Independent work
Returned to Main
1

Basically:

  1. Main calls MyMethodAsync
  2. MyMethodAsync calls LongRunningOperationAsync
  3. LongRunningOperationAsync then calls Task.Delay, but awaits it which suspends further evaluation of the enclosing method LongRunningOperationAsync, returning control to the caller, namely MyMethodAsync.
  4. MyMethodAsync prints out "Independent work".
  5. MyMethodAsync attempts to assign the result of LongRunningOperation to resultbutawaits it, suspends evaluation of the enclosing MyMethodAsync, and returns control of the program back to Main`
  6. Main prints out "Returned to Main"
  7. Task.Delay(1000) in LongRunningOperationAsync() completes
  8. A new thread is spawned, In the caller of LongRunningOperationAsync (MyMethodAsync) the integer 1 is assigned to result.
  9. Evaluation of MyMethodAsync completes, and MyMethodAsync prints out the value of result
  10. Control is given back to Main, which suspends evaluation until the user enters a key via Console.ReadKey

Firstly, is my understanding of how this program evaluates correct? Secondly, why is it that when I comment Console.ReadKey, I get the following unexpected result?

Independent work
Returned to Main

Does the Main method not wait for all threads to get evaluated before exiting out of the program? Why or why not?

CodePudding user response:

The answer to your question "Why or why not?" is complex, but could be answered with a better understanding of a Task.

A Task is not a thread. Many tasks can run on a single thread, and a single task can be run on multiple threads.

A task is more like an event, that will trigger a scheduler to run some code on whatever thread it has available at the time (ignoring some complex issues of continuation)

So your question could be re-phrased to "why does my program not listen to all events and block execution until all have fired?". The answer to that question probably kept 1 or 2 designers of the TPL (Tasks) awake at night, and ultimately they decided that the effects of this decision had the potential to do some serious harm to other types of applications

The designers of TPL did give us a way around this (a few editions of C# later) which is async Main methods. In your case it would look like this:

    static async Task Main(string[] args)
    {
        await MyMethodAsync();
        Console.WriteLine("Returned to Main");
    }
  • Related