清除async&的问题等待?

时间:2018-03-15 18:03:48

标签: c# .net asynchronous async-await

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

混淆伴随着awaitasync的编程,它似乎让我更加困惑,有人可以帮助回答我的一些担忧吗?

我没有及时看到async关键字,只是你想让某个方法让Visual Studio知道该方法可以await并让你发出警告的方法您。如果它有一些其他特殊含义实际影响某些东西,有人可以解释一下吗?

转到await,在与朋友交谈后,我被告知我有一件大事是错的,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上运行了一些异步代码,如果我们深入了解范围并且我们需要访问播放器会发生什么,但它还没有返回播放器?

如果它没有阻止该方法,它如何知道该玩家不是空的,它是否会做一些魔法并等待我们如果我们到达那种情况,或者我们只是禁止自己以这种方式写作方法并自己处理。

4 个答案:

答案 0 :(得分:13)

  

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

我是该功能的设计师之一,我不觉得我甚至接近掌握它,你问的是初级问题并且有一些非常非常错误的想法,所以我怀疑在这里发生了一些狂妄自大。

  

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

假设你问过"为什么有些物质很硬而有些软?"我回答说,物质是由原子排列组成的,有些原子排列很硬,有些是柔软的。虽然这无疑是正确的,但我希望你能够继续这种无益的不解释。

同样,你刚刚取代了模糊的词语"异步"另一个模糊的词"多任务"。这是一个没有解释的解释,因为您还没有明确定义多任务的含义。

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

  

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

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

  

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

它只是使await成为方法内的关键字,并对返回类型设置限制,并将return的含义更改为&#34;表示与此调用关联的任务已完成& #34;,以及其他一些家政细节。

  

await不会阻止当前的方法

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

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

  

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

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

  

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

这不会发生。

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

请记住,等待意味着异步等待,这就是为什么我们称之为&#34; await&#34;。 await是异步工作流中的一个点,在等待任务完成之前工作流无法继续。这就是你描述等待的相反

同样,请记住异步工作流是什么:它是一组任务,其中这些任务彼此依赖。 我们通过在依赖点处放置await来表示一个任务依赖于另一个任务的完成。

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

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

等待意味着&#34;此工作流程的其余部分无法继续,直到获得玩家&#34;。这实际上是对的吗?如果您想要在获取播放器之前执行foreach 而不是,那么这是正确的。但是foreach并不依赖于玩家,因此我们可以这样重写:

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

请参阅,我们将依赖点移至工作流程的后期。我们开始了#34;获得一名玩家&#34;任务,然后我们做foreach,然后我们在需要之前检查播放器是否可用

如果你相信await某种方式&#34;接听电话并使其异步&#34;,这应该消除这种信念。 await执行任务,如果不完整则返回。如果完成,则提取该任务的值并继续。 &#34;获得一名玩家&#34;操作已经异步,await没有这样做。

  

如果它没有阻止该方法,它如何知道该玩家不为空

阻止该方法,或者更确切地说,它暂停方法。

该方法暂停,直到任务完成并且提取值后才会恢复。

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

答案 1 :(得分:3)

  

await不会阻止当前的方法

正确。

  

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

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

答案 2 :(得分:1)

  

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

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

  

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

这根本就不会发生。

async / await非常适合执行各种I / O(文件,网络,数据库,UI),而不会浪费大量线程。线程很贵。

但作为一名程序员,你可以编写(并思考),好像它是全部同步发生的。

答案 3 :(得分:-4)

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

尝试使用Task< T >作为返回数据。