在方法上使用await Task.Factory.StartNew将立即返回

Dav*_*eli 3 c# console-application async-await c#-7.1

我正在运行以下代码(C#7.1控制台应用程序),我无法理解为什么行为上的差异.

如果我等待常规的异步方法调用或Task.Run - 它按预期工作(即应用程序不会立即返回).但是,如果我使用Task.Factory.StartNew - 它将立即返回,而不会实际运行代码.

奇怪的是 - 如果我使用StartNew但是在方法内部删除await,它将不会立即返回...

问题:立即返回:

static async Task Main(string[] args)
{
    await Task.Factory.StartNew(DisplayCurrentInfo);
}

static async Task DisplayCurrentInfo()
{
    await WaitAndApologize();
    Console.WriteLine($"The current time is {DateTime.Now.TimeOfDay:t}");
    Thread.Sleep(3000);
}
Run Code Online (Sandbox Code Playgroud)

即 - 我不会看到打印到控制台的任何内容,控制台已经关闭.

没问题:这不会立即返回:

static async Task Main(string[] args)
{
    await DisplayCurrentInfo(); // or await Task.Run(DisplayCurrentInfo);
}

static async Task DisplayCurrentInfo()
{
    await WaitAndApologize();
    Console.WriteLine($"The current time is {DateTime.Now.TimeOfDay:t}");
    Thread.Sleep(3000);
}
Run Code Online (Sandbox Code Playgroud)

奇怪:这也不会立即返回:

static async Task Main(string[] args)
{
    await Task.Factory.StartNew(DisplayCurrentInfo); 
}

static async Task DisplayCurrentInfo()
{
    WaitAndApologize();
    Console.WriteLine($"The current time is {DateTime.Now.TimeOfDay:t}");
    Thread.Sleep(3000);
}
Run Code Online (Sandbox Code Playgroud)

WaitAndApologize:

static async Task WaitAndApologize()
{
    // Task.Delay is a placeholder for actual work.  
    await Task.Delay(2000);
    // Task.Delay delays the following line by two seconds.  
    Console.WriteLine("\nSorry for the delay. . . .\n");
}
Run Code Online (Sandbox Code Playgroud)

ang*_*son 7

如果您使用Task.Factory.StartNew(MethodThatReturnsTask),则返回a Task<Task<T>>Task<Task>取决于该方法是否返回通用任务.

最终结果是你有2个任务:

  1. Task.Factory.StartNew产生一个调用的任务MethodThatReturnsTask,让我们称这个任务为"任务A"
  2. MethodThatReturnsTask在你的情况下返回一个Task,让我们调用这个"任务B",这意味着使用了StartNew处理它的重载,最终的结果是你得到了一个包装任务B的任务A.

要"正确"等待这些任务需要2等待,而不是1.你的单一等待只是等待任务A,这意味着当它返回时,任务B仍在执行等待完成.

要天真地回答你的问题,请使用2等待:

await await Task.Factory.StartNew(DisplayCurrentInfo);
Run Code Online (Sandbox Code Playgroud)

但是,为什么你需要生成一个任务只是为了启动另一个异步方法,这是值得怀疑的.相反,你最好使用第二种语法,只需等待方法:

await DisplayCurrentInfo();
Run Code Online (Sandbox Code Playgroud)

意见如下:一般来说,一旦你开始编写异步代码,Task.Factory.StartNew当你需要生成一个线程(或类似的东西)来调用与某些东西不同步的东西时,应该保留使用或者它的任何兄弟方法.其他.如果您不需要这种特殊模式,最好不要使用它.