为什么异步等待不按预期工作

Jam*_*mes 0 c# task-parallel-library async-await c#-5.0 .net-4.5

我正在从一个教程中学习TPL(async/await),我尝试使用控制台应用程序自己测试它.请不要被我的无知冒犯.我确定我在某处做错了 - 我写了下面的代码:

static void Main(string[] args_)
{
    Task<int> task = ProcessNumber(25);
    Console.WriteLine(string.Format("{0}: Waiting started...", DateTime.Now));
    task.Wait();
    Console.WriteLine(string.Format("{0}: Waiting ended...", DateTime.Now));
    Console.WriteLine(task.Result);

    Console.WriteLine("Press any key to terminate!");
    Console.ReadLine();
}

static async Task<int> ProcessNumber(int i_)
{
    Thread.Sleep(1000);
    var integer = await IncrementNumber(i_ * 23);
    return integer;
}

static async Task<int> IncrementNumber(int j_)
{
    Thread.Sleep(6000);
    return (j_ + 23) / 23;
}
Run Code Online (Sandbox Code Playgroud)

这是普通的C#控制台代码.我的问题是为什么我得到以下输出:

3/5/2015 5:22:37 PM: Waiting started...
3/5/2015 5:22:37 PM: Waiting ended...
26
Run Code Online (Sandbox Code Playgroud)

"等待开始"和"等待结束"之间是否应该有相当大的时间差距?

UPDATE

在答案之后,我发现Task.Delay(..)和Thread.Sleep(..)不一样,因为它们的工作方式完全不同.这是一个很好的链接,用一个例子来解释.

似乎将TPL视为多线程的另一个框架是错误的.所有的答案都对我有所帮助,所以我投票给了所有人.但是,我选择Jon的答案,因为它是最具说明性的,也是第一个出现的答案.谢谢大家!

Jon*_*eet 5

不,因为实际上你在打印之前已经睡了7秒钟Waiting started.这是发生的事情:

  • 主要调用ProcessNumber
  • ProcessNumber睡眠1秒钟
  • ProcessNumber调用IncrementNumber
  • IncrementNumber睡眠时间为6秒
  • IncrementNumber执行计算,然后返回已完成的任务
  • ProcessNumber等待完成的任务,然后继续
  • ProcessNumber返回已完成的任务
  • 主打印"等待开始"
  • Main等待已完成的任务完成(no-op)
  • 主打印"等待结束"

如果你改变每Thread.Sleep

await Task.Delay(...);
Run Code Online (Sandbox Code Playgroud)

然后你会看到你的期望.新流程将是:

  • 主要调用ProcessNumber
  • ProcessNumber调用Task.Delay并等待结果...
  • ...并且由于尚未完成,ProcessNumber将向Main返回正在进行的任务
  • 主打印"等待开始"
  • Main等待ProcessNumber返回的任务完成
  • Task.Delay任务完成
  • ProcessNumber中的延续开始执行...
  • ProcessNumber调用IncrementNumber
  • IncrementNumber调用Task.Delay并等待结果...
  • ...并且由于尚未完成,IncrementNumber将向ProcessNumber返回正在进行的任务
  • ProcessNumber等待尚未完成的任务,因此继续退出.
  • 第二个延迟任务完成
  • IncrementNumber中的延续执行
  • 它到达IncrementNumber的末尾,并设置相关任务的结果
  • ProcessNumber(正在等待该任务)的延续现在执行
  • 它到达ProcessNumber的末尾,并设置相关任务的结果
  • Main中对Task.Wait的调用完成
  • 主打印"等待结束"等

理解这一点非常重要,直到异步方法遇到await它实际需要暂停的第一个(因为它等待的东西还没有完成),它会同步执行.这不像调用异步方法分成不同的线程.