async、await正确姿势
摘要
async、await是在C# 5.0之后引入的一种简化异步操作的方式。使用它们之后,可以使我们的编写异步操作更加方便快捷,维护以及阅读起来更方便。
一个例子
async、await虽然简化了我们编写异步方法。但也很容易让人产生误解。首先看一个例子:
public static async Task<int> AddAsync(int x, int y) { return await Task.Factory.StartNew(() => { return x + y; }); }
这种加了async、await叫不叫异步呢?答案肯定不是的。我们可以这样叫这种方法:加了async、await标记的同步方法。
再看下面的一个例子
原文:https://docs.microsoft.com/zh-cn/dotnet/csharp/programming-guide/concepts/async/index
// Three things to note in the signature: // - The method has an async modifier. // - The return type is Task or Task<T>. (See "Return Types" section.) // Here, it is Task<int> because the return statement returns an integer. // - The method name ends in "Async." async Task<int> AccessTheWebAsync() { // You need to add a reference to System.Net.Http to declare client. HttpClient client = new HttpClient(); // GetStringAsync returns a Task<string>. That means that when you await the // task you'll get a string (urlContents). 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; // The return statement specifies an integer result. // Any methods that are awaiting AccessTheWebAsync retrieve the length value. return urlContents.Length; }
上面的方法GetStringAsync,如果不需要返回值,可以移步进行执行,然后会执行方法DoIndependentWork方法,知道await getStringTask拿到GetStringAsync方法的返回值。
以下特征总结了使上一个示例成为异步方法的原因。
-
方法签名包含
async
修饰符。 -
按照约定,异步方法的名称以“Async”后缀结尾。
-
返回类型为下列类型之一:
-
如果你的方法有操作数为 TResult 类型的返回语句,则为 Task<TResult>。
-
如果你的方法没有返回语句或具有没有操作数的返回语句,则为 Task。
-
Void
:如果要编写异步事件处理程序。 -
包含
GetAwaiter
方法的其他任何类型(自 C# 7 起)。
有关详细信息,请参见本主题后面的“返回类型和参数”。
-
-
方法通常包含至少一个 await 表达式,该表达式标记一个点,在该点上,直到等待的异步操作完成方法才能继续。 同时,将方法挂起,并且控件返回到方法的调用方。 本主题的下一节将解释悬挂点发生的情况。
在异步方法中,可使用提供的关键字和类型来指示需要完成的操作,且编译器会完成其余操作,其中包括持续跟踪控件以挂起方法返回等待点时发生的情况。 一些常规流程(例如,循环和异常处理)在传统异步代码中处理起来可能很困难。 在异步方法中,元素的编写频率与同步解决方案相同且此问题得到解决。
上面的方法执行过程
关系图中的数值对应于以下步骤。
-
事件处理程序调用并等待
AccessTheWebAsync
异步方法。 -
AccessTheWebAsync
可创建 HttpClient 实例并调用 GetStringAsync 异步方法以下载网站内容作为字符串。 -
GetStringAsync
中发生了某种情况,该情况挂起了它的进程。 可能必须等待网站下载或一些其他阻止活动。 为避免阻止资源,GetStringAsync
会将控制权出让给其调用方AccessTheWebAsync
。GetStringAsync
返回 Task<TResult>,其中TResult
为字符串,并且AccessTheWebAsync
将任务分配给getStringTask
变量。 该任务表示调用GetStringAsync
的正在进行的进程,其中承诺当工作完成时产生实际字符串值。 -
由于尚未等待
getStringTask
,因此,AccessTheWebAsync
可以继续执行不依赖于GetStringAsync
得出的最终结果的其他工作。 该任务由对同步方法DoIndependentWork
的调用表示。 -
DoIndependentWork
是完成其工作并返回其调用方的同步方法。 -
AccessTheWebAsync
已用完工作,可以不受getStringTask
的结果影响。 接下来,AccessTheWebAsync
需要计算并返回该下载字符串的长度,但该方法仅在具有字符串时才能计算该值。因此,
AccessTheWebAsync
使用一个 await 运算符来挂起其进度,并把控制权交给调用AccessTheWebAsync
的方法。AccessTheWebAsync
将Task<int>
返回给调用方。 该任务表示对产生下载字符串长度的整数结果的一个承诺。
总结
简单一句话,并不是所有的加了async和await关键字的方法就是异步方法。可以这样理解 await 的位置决定了到底是不是异步方法,如果直接await xxxAsync那么是挂起当前方法直到拿到返回值才会执行下面的逻辑,这样就是一种同步方法了。异步是类似这样的 代码块:
task t=xxAsync(); //其它业务逻辑 await t; //结束
参考文章
https://docs.microsoft.com/zh-cn/dotnet/csharp/programming-guide/concepts/async/index 这篇文章对async、await介绍的非常详细,不懂的可以参考这个。
-
博客地址:http://www.cnblogs.com/wolf-sun/
博客版权:如果文中有不妥或者错误的地方还望高手的你指出,以免误人子弟。如果觉得本文对你有所帮助不如【推荐】一下!如果你有更好的建议,不如留言一起讨论,共同进步! 再次感谢您耐心的读完本篇文章。