"static *async* Task Main()"是如何开始的?



我正试图弄清楚如何在C#中启动异步主方法。为此,我想创建一个与异步主线程相同的新线程示例。

这就是在没有异步的情况下完成的方式:

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");
}
}

如果我运行上面的代码,将打印以下内容:

Helloooo
Woooorld

您可以看到,我没有添加t.Join(),这是因为t是前台线程。但是,如果尝试对async进行同样的操作,则会发生以下情况:

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");
}
}

Helloooo

打印,并且程序退出,即使新线程是前台线程。现在我明白了这里发生的事情,当新线程到达await Task.Delay(1000);时,它启动一个状态机,并释放线程用于其他事情。我想知道的是,为了让我原来的线程消亡,让我的新线程接管,我必须改变什么。我想了解异步/等待链是如何启动的。

一篇关于C#7.1的文章说:

";通过允许入口点返回Task/Task并标记为async,允许在应用程序的Main/entrypoint方法中使用wait">

public static void Main()
{
MainAsync().GetAwaiter().GetResult();
}
private static async Task MainAsync()
{
... // Main body here
}

";我们可以消除对这个样板的需要,并通过允许Main本身是异步的,从而使waiting可以在其中使用,使其更容易开始

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

正如你所看到的,它们并不是单独的线程,而是";"阻塞";直到MainAsync返回任务IsCompleted。

发生这种情况是由于以下因素的组合:

  • 在Console应用程序中,SynchronizationContext.Currentnot被设置
  • 如果未设置SynchronizationContext.Current,则在ThreadPool线程上调用等待响应(例如,在您的示例中打印"Woooorld"(
  • ThreadPool线程是后台线程

最新更新