【问题标题】:async await code executing not as expected异步等待代码未按预期执行
【发布时间】:2020-02-12 23:11:39
【问题描述】:

我创建了一个简单的示例来理解 C# 中的 async/await。

class Program
{
    static void Main(string[] args)
    {
        var t = BarAsync();
        Console.WriteLine("Main");
    }

    private static async Task BarAsync()
    {
        Console.WriteLine("This happens before await");
        int i = await QuxAsync();
        Console.WriteLine("This happens after await. This result of await is " + i);
    }

    private static Task<int> QuxAsync()
    {
        int c = 0;
        for (int i = 0; i < int.MaxValue; i++)
        {
            c++;
        }
        Console.WriteLine("in the middle processing...");
        return Task.FromResult(c);
    }
}

所以程序首先打印This happens before await,然后计算返回方法中的值。然后打印结果。

看起来不错。我的问题是,因为 await 不会阻塞评估异步方法的线程。我的理解是如果异步需要很长时间它会返回到它的调用方法。

对于我的例子,因为 QuxAsync() 需要很长时间,代码

Console.WriteLine("Main");

没有被阻止,很快就会被评估。 我认为打印顺序应该是

 This happens before await
 Main
 in the middle processing...
 This happens after await. This result of await is 2147483647

但不是,为什么?

【问题讨论】:

  • QuxAsync 中的任何内容都实际上是异步的,仅仅因为它被标记为异步,并不会使其异步。您的操作同步运行,这就是打印顺序如此的原因。
  • 我无法解释您要问的这个确切原因,但我可以为WebApi's 解释一个场景 我仍然对在控制台应用程序中运行await / async 代码有一个模糊的理解不必经常这样做。当我这样做时,我会使用Task.Result() 来获得结果。无论如何,await Task&lt;T&gt; 将允许当前线程执行被释放回线程池并让另一个线程使用它,一旦工作完成,任务调度程序然后分配相同的线程或如果.ConfigureAwait(false) 是可用线程使用并继续工作。它确实阻塞了线程
  • async 不代表并行,Task 不代表线程。通过创建示例来学习是很棒的,但是如果您必须对所有未按您想象的方式发生的事情提出问题,那么您将走上一条漫长而痛苦的道路,我建议您先研究async and await。浏览stackoverflow并检查所有异步和等待标签/问题和答案。查看 Stephen Clearys 和 Stephen Toubs 的博客
  • @MickyD:并发和异步不是一回事;异步工作流不必是并发的。例如,考虑“按喇叭,然后异步等待一秒钟,然后打开门”的工作流,并且在异步等待期间可以在 10 毫秒内处理一条消息。您不需要第二个工作线程来实现此异步流程。
  • @Hello:实验是个好主意,特别是因为它表明你对异步有一些错误的想法。这是获得正确信念的好机会。我同意上面的建议,即您首先要多阅读。我还想在 GUI 应用程序中进行实验添加到该建议。控制台应用程序具有不寻常的异步工作流线程上下文,这可能会造成混淆。 GUI 应用程序可以更轻松地了解如何使用单线程实现异步工作流。

标签: c# async-await task-parallel-library


【解决方案1】:

我会第二(第三?)其他人的建议,您继续阅读和了解async。我偏爱我自己的async intro,但现在有很多不错的。

我的问题是,因为 await 不会阻塞评估异步方法的线程。我的理解是如果异步需要很长时间它会返回到它的调用方法。

这是错误的部分。异步与某件事需要多长时间完全无关。

缺少两条知识,都包含在我的async intro 中。

首先:await 首先检查它的参数。如果已经完成,则await 继续执行 - 同步

第二:每个方法都被同步调用。包括async 方法。唯一发生异步的情况是async 方法有一个await,其参数已经完成;在这种情况下,async 方法返回一个不完整的任务。

将这两者放在一起应该可以解释为什么您的代码实际上是同步运行的。

【讨论】:

  • 感谢您的宝贵时间。我会阅读你的信息。但在此之前,我怎么知道论证是否完成?
  • @Hello: Task.FromResult 返回一个已完成的任务。
  • 在后台生成了一个状态机。在结构体的MoveNext 方法中,TaskAwaiter.IsCompleted 用于返回与否。我假设等待的第一个论点就是它。不知道 Task complected 和这个论点有什么关系(TaskAwaiter.IsCompleted)?
  • @Hello:是的,为BarAsync 生成了一个状态机。当执行该状态机的第一个状态时,BarAsync 中的代码将运行,包括对QuxAsync 的调用并获取其返回值 (Task.FromResult)。状态机(作为await 的一部分)然后检查任务是否已完成(已完成),因此它继续执行,从BarAsync 返回已完成的任务。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2019-04-09
  • 2011-12-15
  • 2019-04-19
  • 1970-01-01
  • 2020-03-04
  • 2019-04-07
相关资源
最近更新 更多