C#中的await async如何工作?

56

我试图理解C#中await async的工作方式,有一件事令我很困惑。我知道任何使用await关键字的方法必须标记为async。

问题1:这个假设是正确的还是在await关键字下面的代码仍然执行?

其次,假设我调用了一个异步服务方法并需要返回其结果。返回语句在await关键字下面。

问题2:return语句是在异步调用完成之后还是之前执行的?

问题3:我想使用该服务调用的结果,但异步操作无法帮助我,因为我希望在结果返回时才触发调用方法。我知道可以使用Result属性来使调用同步。但是数据库操作中使用async有什么用处,因为它们实际上占据了大多数应用程序的80%的时间。

问题4:我如何在数据库操作中使用async?是否可行和推荐?

问题5:在哪些场景下将使用异步操作是有用的?似乎每个API现在都只是制作异步操作,没有理由吗?我有没有错过使用异步操作的重点?

我所说的API没有理由制作异步方法是因为方法必须返回某些内容,而在进行计算之前它们如何返回呢?因此,本质上调用是否仍然会被阻塞直到结果返回?


5
你提了很多问题。如果你只问一个明确的问题,SO会更有效。如果你有多个问题,请分别发布它们。 - svick
1
@Svick 我的看法是,MDSN 把我搞得更糊涂了,让我心里有很多问题。如果其他人也有同样的问题,也许他们可以看到这个帖子,更好地理解异步操作。我所有的问题都与一个主题相关,在我看来,发布5个不同的问题,希望有人能全部看到它们是没有意义的... - Afraz Ali
Stack Overflow是关于分享你的知识,对吧?唯一的规则就是问题必须明确且可回答。 - Afraz Ali
1
@SyedAfrazAliRizvi 发布多个问题的问题在于,可能会有不同的人回答你的不同问题。这种情况下,你如何判断哪个答案应该被接受? - svick
2个回答

60

MSDN 解释了一切

我知道有时候普通的文档(特别是来自MSDN的)可能很难应用到你的特定情况中,所以让我们来看看你的问题。

问题#1:这个假设是正确的还是在“await”关键字下面的代码仍然被执行?

“await”关键字下面的代码只有在异步调用完成后才会被执行。同时,由于你的方法被标记为“async”,控制权将被返回给调用你的方法的调用者,直到你的方法完成。 来自上面的MSDN链接:

Task<string> getStringTask = client.GetStringAsync("http://msdn.microsoft.com");

// You can do work here that doesn't rely on the string from GetStringAsync.
DoIndependentWork();

// The await operator suspends AccessTheWebAsync. 
//  - AccessTheWebAsync can't continue until getStringTask is complete. 
//  - Meanwhile, control returns to the caller of AccessTheWebAsync. 
//  - Control resumes here when getStringTask is complete.  
//  - The await operator then retrieves the string result from getStringTask. 
string urlContents = await getStringTask;

我认为这些注释已经很详细了。

其次,假设我调用了一个异步服务方法并需要返回结果。返回语句位于 await 关键字下面。

问题 #2:当异步调用完成后,返回语句何时执行,是在之后还是之前?

之后执行。

问题 #3:我想使用该服务调用的结果,但异步操作无效,因为我希望在返回结果后调用方法。我知道可以使用 Result 属性来实现同步调用。但是,那么 DB 操作中的异步调用有什么用处呢?毕竟它们是大多数应用程序中实际上占用了 80% 的时间。

假设您需要进行三个不相关的 DB 查询才能完成服务,然后根据结果执行计算,最后完成。如果按顺序执行,则必须等待每个操作完成。如果使用异步调用,则 C# 将并行运行三个查询,您的服务可能会更快地完成。

此外,返回 Task 的操作可以用作 Futures。请参见 MSDN 上的 Futures,其中讨论了几种基于 Futures 并行化工作并合并结果的模式。

如果您的服务只需要一个 DB 调用,则异步调用肯定会更糟。

问题 #4:如何在 DB 操作中使用异步?是否可行且推荐?

现在 ADO.NET 包括异步方法 ReadAsync 和 NextResultAsync

这是可行的,至于推荐方面,这篇讨论比我能写的更完整 here

问题 #5:在哪种情况下,异步操作是有用的?似乎现在每个 api 都在做异步操作,但没有明显的原因?或者我错过了使用异步操作的重点?

异步操作非常有用,可以轻松地并行化任何长时间运行的操作,而不会遇到线程问题。如果您的方法只做一件事,或一系列简单(快速)的事情,那么是无用的去异步化它们。但是,如果您有多个长时间运行的操作,则通过异步进行并行处理比管理线程要容易得多,也减少了出错的可能性。


2
哇!谢谢伙计,你用非常易懂的方式解释了一切。我看了MSDN,但是看到一个有很多数字的图表来解释哪一行将会执行,这让我更加困惑。感谢你的回复! - Afraz Ali
1
你引用的代码中提到了"AccessTheWebAsync",但在所引用的代码中并未定义或包含该方法。可以假设这是一个容器方法。 - Manachi

15

大部分问题的答案都可以在官方 文档和我写的介绍文章中找到。

问题 # 4:如何在数据库操作中使用异步?这可行吗?

Entity Framework 6(目前处于测试版)支持async。较低级别的数据库API以一种或另一种方式支持异步操作。其中一些(例如SQLite)直接支持async;其他需要您编写简单的async-兼容包装器。

... 推荐使用吗?

是的,除非您正在编写一个与后端不可扩展的单个数据库机器通信的前端服务器(例如ASP.NET)。在这种特定情况下,没有必要让您的前端扩展,因为您的后端无论如何都无法扩展到匹配它。

在哪些情况下,异步操作会有用呢?现在似乎每个API都只是为了做异步操作,这是为什么?还是我错过了使用异步操作的重点?异步操作的好处有:1.在客户端(UI)上,您的应用程序保持响应;2.在服务器端,您的应用程序更具可扩展性。

在你的回答结尾简单而完美地陈述好处。 - joelc

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