【问题标题】:Clearing up an issue with async & await?清除异步和等待的问题?
【发布时间】:2018-08-24 16:07:09
【问题描述】:

我最近一直在学习异步编程,我想我已经掌握了。异步编程很简单,只需让我们的程序进行多任务处理即可。

awaitasync 编程带来了困惑,这似乎让我更加困惑,有人可以帮助回答我的一些问题吗?

我没有看到async 关键字那么多,只是您使用一种方法让Visual Studio 知道该方法可能await 某些东西并允许它警告您。如果它有其他一些实际影响某事的特殊含义,有人可以解释一下吗?

转到await,在与朋友交谈后,我被告知我有 1 大问题,await 不会阻止当前方法,它只是执行该方法中留下的代码并执行异步操作自己的时间。

现在,我不确定这种情况发生的频率,但假设你有一些这样的代码。

Console.WriteLine("Started checking a players data.");

var player = await GetPlayerAsync();

foreach (var uPlayer in Players.Values) { 
    uPlayer.SendMessage("Checking another players data");
}

if (player.Username == "SomeUsername") { 
    ExecuteSomeOperation();
}

Console.WriteLine("Finished checking a players data.");

如您所见,我在GetPlayerAsync 上运行了一些异步代码,如果我们更深入地了解范围并且我们需要访问播放器,但它还没有返回播放器,会发生什么?

如果它没有阻塞该方法,它如何知道 player 不是 null,它是否会做一些魔术并在我们遇到这种情况时等待我们,或者我们是否只是禁止自己以这种方式编写方法自己处理。

【问题讨论】:

  • 你想检查播放器是否为空?
  • 如果不是呢?我猜你被搞砸了?
  • 你到底想要什么?如果您想检查播放器的值,那么我假设您可以执行If (player.Value == null) 之类的操作(我不知道您的播放器具有哪些属性,但如果它具有value property 它可能会起作用)
  • "异步编程很简单,只是允许我们的程序进行多任务处理。" No, asynchrony and parallelism are two very different things.。异步编程是指操作立即返回,并在返回后执行一些工作,可能会在底层操作实际完成时通知调用者。它与同步做事相反。这与并行性无关。
  • async 在方法上将把它变成一个状态机,在awaits 之后运行代码作为延续。如果你想使用await,它也是必需的。

标签: c# .net asynchronous async-await


【解决方案1】:

我最近一直在学习异步编程,我想我已经掌握了。

我是该功能的设计者之一,但我什至觉得自己还没有接近掌握它,而且你问的是初学者级别的问题,并且有一些非常非常错误的想法,所以有些自大我怀疑在这里。

异步编程只是让我们的程序进行多任务处理。

假设您问“为什么有些物质是硬的,有些是软的?”我回答“物质是由原子排列组成的,有些原子排列是硬的,有些是软的”。虽然这无疑是真的,但我希望你能反驳这种无益的非解释。

同样,您刚刚用另一个模糊的词“多任务”替换了模糊的词“异步”。这是一个解释不了什么的解释,因为您还没有明确定义多任务的含义。

异步工作流无疑是关于执行多个任务。 这就是为什么工作流中的基本工作单元是Task<T> monad。异步工作流是通过构建多个任务之间的依赖关系图来组合多个任务。但这并没有说明 该工作流是如何在软件中实际实现的。这是一个复杂而深刻的主题。

我没有看到太多 async 关键字,它只是让 Visual Studio 知道该方法可能等待某事并让您允许它警告您的方法。

这基本上是正确的,但不要认为它告诉 Visual Studio; VS 不在乎。你说的是 C# 编译器。

如果它有其他一些实际影响某事的特殊含义,有人可以解释一下吗?

它只是将await作为方法内部的关键字,并对返回类型进行限制,并将return的含义更改为“表示与此调用相关的任务已完成”,以及其他一些内务详情。

await 不会阻塞当前方法

当然可以。为什么你会认为它没有?

它不会阻塞线程,但它肯定会阻塞方法

它只是执行留在该方法中的代码并在自己的时间执行异步操作。

绝对不是。这完全是倒退。 await 与此相反。 Await 表示如果任务未完成,则返回给您的调用者,并注册此方法的其余部分作为任务的继续

如您所见,我在 GetPlayerAsync 上运行了一些异步代码,如果我们更深入地了解范围并且我们需要访问播放器,但它还没有返回播放器,会发生什么?

这永远不会发生。

如果await执行时分配给player的值不可用,则等待返回,当该值时,方法的其余部分恢复可用(或任务异常完成时。)

记住,await 的意思是异步等待,这就是我们称之为“await”的原因。 等待是异步工作流中的一个点,在等待的任务完成之前工作流无法继续进行。这与您描述等待的方式相反

再次记住什么是异步工作流:它是任务的集合,其中这些任务相互依赖。 我们通过在依赖点放置await 来表示一项任务依赖于另一项任务的完成

让我们更详细地了解您的工作流程:

var player = await GetPlayerAsync();
foreach (var uPlayer in Players.Values) ...
if (player.Username == "SomeUsername") ...

等待的意思是“在获得玩家之前,此工作流程的其余部分无法继续”。这真的正确吗?如果您希望foreach 在获取播放器之前执行,那么这是正确的。但是 foreach 不依赖于播放器,所以我们可以这样重写:

Task<Player> playerTask = GetPlayerAsync();
foreach (var uPlayer in Players.Values) ...
Player player = await playerTask;
if (player.Username == "SomeUsername") ...

看,我们将依赖点移到了工作流程的后面。我们启动“获取玩家”任务,然后执行 foreach,然后然后在我们需要之前检查玩家是否可用

如果您认为await 以某种方式“接听电话并使其异步”,那么这应该消除这种信念。 await 接受一个任务如果未完成则返回。如果完成,则提取该任务的价值并继续。 “获取玩家”操作已经是异步的,await 没有做到这一点。

如果它没有阻塞该方法,它怎么知道玩家不是空的

确实阻塞了该方法,或者更准确地说,它暂停该方法。

该方法挂起并且在任务完成并提取值之前不会恢复

它不会阻塞线程。它返回,以便调用者可以继续在 不同 工作流程中工作。任务完成后,将在当前上下文中安排继续,方法将恢复。

【讨论】:

  • 如果async 只启用await 并限制返回类型,是否可以从async Task&lt;int&gt; Execute() {return 2;} 中删除async? :)
  • @Karol:好点,我忽略了小事。我会修改文本。
  • 非常感谢您写这篇文章,它确实为我清除了它,谢谢!
  • @EricLippert 能否请您为您撰写的 asnyc await 博客或您知道的一些好博客提供一些链接参考。我想了解更多关于异步等待的信息。
【解决方案2】:

await 不会阻塞当前方法

正确。

它只是执行留在该方法中的代码并在自己的时间执行异步操作。

不,一点也不。它安排方法的其余部分在异步操作完成时运行。它不会立即运行该方法的其余部分。 不允许在等待的操作完成之前运行方法中的任何其余代码。它只是不阻塞进程中的当前线程,当前线程返回给调用者,并且可以去做任何它想做的事情。当异步操作完成时,该方法的其余部分将由同步上下文(或线程池,如果不存在)调度。

【讨论】:

  • 很好的解释!不过有两件事:a) 我认为同意第一个引用 --await 不会阻止当前的 方法--可能有点误导 (尽管您稍后会澄清它不会阻塞的是当前的 thread)。 b)我认为如果您将上面的评论(关于异步与并行性)作为答案的一部分包括在内,会更有帮助。
【解决方案3】:

我有 1 个主要错误,await 不会阻塞当前方法,它只是执行留在该方法中的代码并在自己的时间执行异步操作。

但它确实阻塞了该方法,因为调用await 的方法在结果出现之前不会继续。它只是不会阻塞该方法所在的线程继续运行。

...我们需要访问播放器,但它还没有返回播放器?

这根本不会发生。

async/await 非常适合在不浪费大量线程的情况下执行各种 I/O(文件、网络、数据库、UI)。线程很昂贵。

但作为一名程序员,您可以编写(和思考)就好像这一切都是同步发生的。

【讨论】:

    【解决方案4】:

    在此代码中,您不会使用 Await,因为 GetPlayerAsync() 运行一些异步代码。可以从 Async 和 Await 的不同角度来考虑,“Async”是在等待,“Await”是异步操作。

    尝试使用Task&lt; T &gt;作为返回数据。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2021-10-11
      • 1970-01-01
      • 2018-12-09
      • 2017-04-01
      • 2021-05-24
      • 1970-01-01
      • 1970-01-01
      • 2022-10-25
      相关资源
      最近更新 更多