4

我正在试验新的 async 和 await 关键字。我生成了以下异步函数:

private async static Task<string> GetStringAsync(string pageAddress)
{
    HttpClient client = new HttpClient();
    return client.GetStringAsync(pageAddress);
}

我知道我正在返回 aTask<String>并且可以等待其他方法的结果。这种方法效果很好。我的问题是当我将上述函数的第二行替换为以下内容时会发生什么(实际上是在幕后)(注意await关键字的引入):

return await client.GetStringAsync(pageAddress);

该函数的行为方式完全相同!记住函数返回Task<string>not string。这里的await关键字是退化的吗?编译器是否只是从我的代码中删除它?

4

3 回答 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/

从底部开始。一旦你理解了continuation的概念,你就可以阅读我关于我们如何设计异步特性的系列文章:

http://blogs.msdn.com/b/ericlippert/archive/tags/async/

于 2013-07-06T15:27:41.120 回答
6

正如 Eric Lippert 指出的那样,第一个版本无法编译。您必须删除async关键字,否则您会收到类型错误。

下面是一个有用的心理模型,关于asyncandawait关键字如何与返回类型一起工作:

  • T方法返回的任何值async都被“包装”到Task<T>.
  • 关键字(您可以将await其视为运算符)应用于 a 时Task<T>,将“解包”它,从而产生 type 的值T

现在,这是一个极端的简化;实际发生的事情要复杂得多。例如,这种简化跳过了await当前的工作方式SynchronizationContext:在第二个示例中,该方法将在完成后尝试返回原始上下文await,因此如果该上下文繁忙,您将观察到不同的行为。

但在大多数情况下,这两个例子几乎是等价的。由于async状态机和上下文恢复,第二个效率较低。

我有一个async/await介绍,您可能会觉得有帮助;在那篇文章中,我尝试以一种不太复杂但实际上也不正确async的方式进行解释。:)

于 2013-07-06T18:21:49.563 回答
2

埃里克显然是这里的专家,他的建议是合理的,但要回答你的具体问题:

在第一个版本中,async方法上的关键字是无关紧要的,您的GetStringAsync方法返回Task<string>client.GetStringAsync.

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

所以对于 的调用者GetStringAsync,它们在功能上是相同的,但第一个版本更干净。

于 2013-07-06T16:19:17.660 回答