我正在尝试使用新的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
关键字是否已退化?编译器是否只是从我的代码中删除它?
答案 0 :(得分: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/
从底部开始。一旦理解了 continuation 的概念,您就可以阅读我关于如何设计异步功能的一系列文章:
答案 1 :(得分:6)
正如Eric Lippert指出的那样,第一个版本将无法编译;您必须删除async
关键字,否则会出现类型错误。
这是关于async
和await
关键字如何与返回类型一起使用的有用心理模型:
T
方法返回的任何值async
都会“包装”到Task<T>
。await
关键字(您可以将其视为运算符),当应用于Task<T>
时,将“解包”它,从而产生类型为T
的值。< / LI>
现在,这是一种极端的简化;实际发生的事情更复杂。例如,此简化会跳过await
如何使用当前SynchronizationContext
:在第二个示例中,该方法将尝试在await
完成后返回到原始上下文,因此您将观察如果上下文繁忙,则会有不同的行为。
但在大多数情况下,这两个例子几乎相同。由于async
状态机并且在上下文中恢复,第二个效率较低。
我有async
/await
intro您可能会觉得有帮助;在该帖子中,我尝试以一种非太复杂但实际上不正确的方式解释async
。 :)
答案 2 :(得分:2)
Eric显然是这里的专家,他的建议很合理,但要回答你的具体问题:
在第一个版本中,该方法的async
关键字无关紧要,而您的GetStringAsync
方法返回的Task<string>
等同于client.GetStringAsync
所返回的async
。
在第二个版本中,该方法的await
关键字是必需的,因为您在方法中使用await
并且Task<string>
关键字创建并返回单独的client.GetStringAsync
一旦等待await
完成,就等待完成。发生这种情况时,client.GetStringAsync
将计算为GetStringAsync
异步获取的字符串,该字符串将作为异步方法的结果返回。
所以对于{{1}}的来电者来说,它们在功能上是相同的,但第一个版本更清晰。