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

Why the async await not working as expected

我正在通过教程学习 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;
}

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

3/5/2015 5:22:37 PM: Waiting started...
3/5/2015 5:22:37 PM: Waiting ended...
26

"Waiting started"和"Waiting Ended"之间应该有相当大的时间间隔吧?

更新

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

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

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

事实上,您应该收到 IncrementNumber 数字的编译器警告,说 This async method lacks 'await' operators and will 运行 synchronously...等。它基本上说您的方法将同步执行。 This answer explains it in detail.

所以当 ProcessNumber returns 整个嵌套方法调用已经同步完成。

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

不,因为事实上你在打印之前睡了 7 秒 Waiting started。事情是这样的:

  • 主要调用 ProcessNumber
  • ProcessNumber 休眠 1 秒
  • ProcessNumber 调用 IncrementNumber
  • IncrementNumber 休眠 6 秒
  • IncrementNumber 执行计算,然后 returns 完成任务
  • ProcessNumber 等待完成的任务,并继续
  • ProcessNumber returns 一个已完成的任务
  • 主要版画"Waiting started"
  • Main 等待已经完成的任务完成(无操作)
  • 主要版画"Waiting ended"

如果将每个 Thread.Sleep 更改为

await Task.Delay(...);

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

  • 主要调用 ProcessNumber
  • ProcessNumber 调用 Task.Delay 并等待结果...
  • ... 并且由于它尚未完成,ProcessNumber returns 是 Main
  • 的一项持续任务
  • 主要版画"Waiting started"
  • Main 等待 ProcessNumber 返回的任务完成
  • Task.Delay 任务完成
  • ProcessNumber 中的延续开始执行...
  • ProcessNumber 调用 IncrementNumber
  • IncrementNumber 调用 Task.Delay 并等待结果...
  • ...并且因为它尚未完成,IncrementNumber returns ProcessNumber
  • 的一项持续任务
  • ProcessNumber 等待尚未完成的任务,因此继续退出。
  • 第二个延迟任务完成
  • IncrementNumber 内的继续执行
  • 到达IncrementNumber的末尾,设置关联任务的结果
  • ProcessNumber 中的继续(正在等待该任务)现在执行
  • 到达ProcessNumber的末尾,设置关联任务的结果
  • Main 中对 Task.Wait 的调用完成
  • 主要版画"Waiting ended"等

真正重要的是要理解,直到异步方法到达第一个 await 它实际需要暂停的地方(因为它正在等待的事情尚未完成),它才会同步执行。这不像调用异步方法会分裂到不同的线程中。

您需要重构您的代码。像这样

    static async Task<int> IncrementNumber(int j_)
{
    await Task.Delay(...);
    return (j_ + 23) / 23;
}
  1. 带有关键字 async 的函数必须在函数体内至少有一个 await 为了 运行 函数异步。
  2. Thread.Sleep(...) 也负责这个。
  3. 如果你发现 CPU 绑定任务而不是 Thread.Sleep 那么你需要做这样的事情。

    //it will create a new thread then sleep.
    await Task.Run( () => (  CPUBoundTask());
    var integer = await IncrementNumber(i_ * 23);
    return integer;