C# 为什么异步等待未按预期工作

C# 为什么异步等待未按预期工作,c#,task-parallel-library,async-await,.net-4.5,c#-5.0,C#,Task Parallel Library,Async Await,.net 4.5,C# 5.0,我正在从教程中学习TPL(async/await),并尝试使用控制台应用程序对其进行测试。请不要因为我的无知而生气。 我确信我在某个地方做错了-我编写了以下代码: static void Main(string[] args_) { Task<int> task = ProcessNumber(25); Console.WriteLine(string.Format("{0}: Waiting started...", DateTime.Now)); task

我正在从教程中学习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;
}
“等待开始”和“等待结束”之间不应该有相当大的时间间隔吗

更新

在回答之后,我发现Task.Delay(..)和Thread.Sleep(..)不一样,因为它们的工作方式非常不同。 下面是一个很好的例子

似乎把TPL看作是多线程的另一个框架是错误的。所有的答案都对我有帮助,所以我投了所有人的票。然而,我选择乔恩的答案,因为它是最具说明性的,也是第一个出现的答案。
谢谢大家

这是因为您的方法都不是异步的。它们通过返回
Task
假装异步,但它们都是同步执行的

事实上,您应该会收到编译器警告,指出此异步方法缺少'await'运算符,并且将同步运行。
IncrementNumber
number。。。它基本上说你的方法是同步执行的

因此,当
ProcessNumber
返回整个嵌套方法时,调用已经同步完成


将方法更改为使用
wait Task.Delay
而不是
Thread.Sleep
使其异步。

否,因为实际上在打印
等待开始之前,您正在睡眠7秒钟。下面是发生的情况:

  • 主叫号码
  • ProcessNumber休眠1秒
  • ProcessNumber调用IncrementNumber
  • 递增数字睡眠6秒
  • IncrementNumber执行计算,然后返回已完成的任务
  • ProcessNumber等待完成的任务,并继续
  • ProcessNumber返回已完成的任务
  • 主打印“等待开始”
  • Main等待已完成的任务完成(无操作)
  • 主打印“等待结束”
如果将每个
线程。Sleep
更改为

await Task.Delay(...);
然后你会看到你所期待的。新流程将是:

  • 主叫号码
  • ProcessNumber调用Task.Delay并等待结果
  • 。。。由于尚未完成,ProcessNumber会将正在进行的任务返回给Main
  • 主打印“等待开始”
  • Main等待ProcessNumber返回的任务完成
  • 任务。延迟任务完成
  • ProcessNumber中的延续开始执行
  • ProcessNumber调用IncrementNumber
  • IncrementNumber调用任务。延迟并等待结果
  • 。。。因为它还没有完成,IncrementNumber将一个正在进行的任务返回给ProcessNumber
  • ProcessNumber等待尚未完成的任务,因此继续操作将退出
  • 第二个延迟任务完成
  • 执行IncrementNumber中的延续
  • 它到达IncrementNumber的末尾,并设置关联任务的结果
  • ProcessNumber中的continuation(正在等待该任务)现在执行
  • 它到达ProcessNumber的末尾,并设置关联任务的结果
  • 对Task.Wait in Main的调用已完成
  • 主要印刷品“等待结束”等

理解这一点非常重要,在异步方法到达实际需要暂停的第一个
await
位置之前(因为它等待的东西还没有完成),它是同步执行的。这不像调用异步方法分裂成不同的线程。

您需要重构代码。像这样

    static async Task<int> IncrementNumber(int j_)
{
    await Task.Delay(...);
    return (j_ + 23) / 23;
}

谢谢Jon的回答-等待任务。延迟(…)解决方案非常有效。除此之外,我还更改了Thread.Sleep代码:静态异步任务SleepForMilSec(int milSec_){Thread.Sleep(milSec_);现在我使用wait SleepForMilSec(..)而不是ProcessNumber和IncrementNumber方法中的直接Thread.Sleep调用。仍然只有6秒的延迟。这意味着:ProcessNumber方法中的第一个Wait调用将被阻止。有什么想法吗?编辑:顺便说一句,你在Pluralsight中关于async Wait的课程是我将遵循的下一个教程。:@James:your
SleepForMilSec
方法应该有一个警告,说明你有一个异步方法没有等待,因此它将同步执行。注意:)是的..我注意到了警告,我也更新了问题!谢谢Jon。这非常有用!始终注意编译器警告。对于这段代码,编译器将给你一个警告,告诉你到底出了什么错。
    static async Task<int> IncrementNumber(int j_)
{
    await Task.Delay(...);
    return (j_ + 23) / 23;
}
//it will create a new thread then sleep.
await Task.Run( () => (  CPUBoundTask());
var integer = await IncrementNumber(i_ * 23);
return integer;