当您在返回任务之前等待任务时会发生什么?

4
我正在尝试使用新的async和await关键字。我编写了以下异步函数:
private async static Task<string> GetStringAsync(string pageAddress)
{
    HttpClient client = new HttpClient();
    return client.GetStringAsync(pageAddress);
}

我知道我正在返回一个 Task<String> ,可以从另一个方法中等待结果。 这个方法很好用。 我的问题是,当我将上面的函数的第二行替换为以下内容时会发生什么(请注意引入了 await 关键字):

return await client.GetStringAsync(pageAddress);

这个函数的行为完全相同!请记住,该函数返回的是Task<string>而不是string。这里的await关键字是否退化了?编译器是否会从我的代码中删除它?


请查看此链接:http://blogs.msdn.com/b/csharpfaq/archive/2012/06/26/understanding-a-simple-async-program.aspx - Mahmoud Darwish
4
第一个版本编译通过了吗? - Eric Lippert
3个回答

7
这个问题的答案过于庞大,可能超出您当前的理解水平。您应该先阅读我的MSDN文章和Mads的MSDN文章; 它们是该功能的良好入门介绍,Mads描述了它的实现方式。您可以在此处找到链接: http://blogs.msdn.com/b/ericlippert/archive/2011/10/03/async-articles.aspx 然后,如果您对该功能的理论感兴趣,您应该从阅读我关于“续延传递风格”的所有文章开始: http://blogs.msdn.com/b/ericlippert/archive/tags/continuation+passing+style/ 从下面开始。一旦您理解了“续延”这个概念,您就可以阅读我的一系列关于如何设计异步功能的文章: http://blogs.msdn.com/b/ericlippert/archive/tags/async/

Jon Skeet的异步系列也非常不错:http://msmvps.com/blogs/jon_skeet/archive/tags/Eduasync/default.aspx - argaz
我打算在接下来的几天里阅读你和Jon的博客文章。谢谢。 - Caster Troy

6
正如Eric Lippert所指出的,第一个版本无法编译;您必须删除async关键字,否则将会出现类型错误。
以下是一个有用的关于asyncawait关键字如何与返回类型配合使用的心理模型:
任何由async方法返回的值T都会被"包装"成一个Task<T>
当应用于Task<T>时,await关键字(可以视为运算符)将"解封"它,结果得到类型为T的值。
这只是一个极度简化的模型;实际发生的事情更加复杂。例如,这种简化省略了await如何与当前的SynchronizationContext配合工作:在第二个示例中,方法将尝试在await完成后返回到原始上下文,因此如果该上下文繁忙,则会观察到不同的行为。
但就大多数情况而言,这两个示例几乎是等效的。第二个示例由于async状态机和在上下文中恢复的原因而不太有效率。
我有一篇介绍async/await的博客文章,您可能会发现有帮助;在该文章中,我试图以不太复杂但也不完全不正确的方式解释async。 :)

2

Eric显然是这里的专家,他的建议很好,但为了回答你具体的问题:

在第一个版本中,方法上的async关键字是无关紧要的,你的GetStringAsync方法返回与client.GetStringAsync相同的Task<string>可等待对象。

在第二个版本中,方法上的async关键字是必需的,因为你在方法中使用了await关键字,await关键字会创建并返回一个独立的Task<string>可等待对象,该对象会在client.GetStringAsync的可等待对象完成时完成。当发生这种情况时,await将计算出由client.GetStringAsync异步获取的字符串,该字符串作为你异步方法的结果返回。

因此,对于GetStringAsync的调用者来说,它们在功能上是相同的,但第一个版本更加简洁。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接