Home > front end >  How is the "static *async* Task Main()" started?
How is the "static *async* Task Main()" started?

Time:10-24

I'm trying to figure out how an async main method is started in C#. To do this I would like to create an example of a new Thread that is the same as the async main thread.

This is how it is done without async:

class Program
{
    public static void Main(string[] args)
    {
        Thread t = new Thread(Main2)
        { IsBackground = false };
        t.Start();
    }

    public static void Main2()
    {
        Console.WriteLine("Helloooo");
        Thread.Sleep(1000);
        Console.WriteLine("Woooorld");
    }
}

If I run the code above, the following is printed:

Helloooo
Woooorld

You can see that I don't add a t.Join() that's because t is a foreground thread. But if try to do the same with async the following happens:

class Program
{
    public static void Main(string[] args)
    {
        Thread t = new Thread(Main2)
        { IsBackground = false };
        t.Start();
    }

    // Can't use "public static async Task main2"
    // because you need to pass in a void method to a new thread
    public static async void Main2()
    {
        Console.WriteLine("Helloooo");
        await Task.Delay(1000);
        Console.WriteLine("Woooorld");
    }
}

Only

Helloooo

Is printed, and the program exits, even though the new thread is a foreground thread. Now I understand what is happening here, When the new thread reaches await Task.Delay(1000); It starts up a state machine and releases the thread for other things. What I want to know is what I have to change to let my original thread die, and let my new thread take over. I want to understand how the async/await chain gets started.

CodePudding user response:

An article about C# 7.1 says:

"Allow await to be used in an application's Main / entrypoint method by allowing the entrypoint to return Task / Task and be marked async."

public static void Main()
{
    MainAsync().GetAwaiter().GetResult();
}

private static async Task MainAsync()
{
    ... // Main body here
}

"We can remove the need for this boilerplate and make it easier to get started simply by allowing Main itself to be async such that awaits can be used in it."

Source: https://learn.microsoft.com/en-us/dotnet/csharp/language-reference/proposals/csharp-7.1/async-main

As you can see, they don't spin up a separate thread, but "blocking" the current thread until the MainAsync returning task IsCompleted.

CodePudding user response:

This happens because of the following combination of factors:

  • In a Console application, SynchronizationContext.Current not is set.
  • If SynchronizationContext.Current not is set, await responses (e.g. printing "Woooorld" in your example) are invoked on a ThreadPool thread.
  • ThreadPool threads are background threads.

CodePudding user response:

async Task Main() is rewritten by compiler simply like this:

static void Main() {
    YourAsyncMain().GetAwaiter().GetResult();
}

static async Task YourAsyncMain() {
    Console.WriteLine("Test");
    Console.ReadKey();
}

No additional threads are needed, you have main thread already which executes your async main function and blocks until it is completed.

  • Related