异步/等待对我们其他人来说
为什么选择异步?
异步关键字
等待关键词
全程异步
结论
保持联系
如何规划你的软件开发职业生涯
由 Mux 赞助的 DEV 全球展示挑战赛:展示你的项目!
asyncC# 中的`and`到底是什么await?为什么 2018 年的 .Net 开发人员还需要了解它是什么以及如何使用它?
也许你已经用过了async/await,但发现自己不得不重新摸索一遍?没关系——我承认我也不是什么async/await专家。
我不得不通过惨痛的教训才明白,例如,使用ConfigureAwait(false).GetResult()(正如许多人可能建议的那样)并不能神奇地使你的异步方法“同步工作”。
async/await但这并不是对内部构造以及如何进行高级操作的深入研究async/await。
这就是“面向大众的Async/await”。“大众”指的是谁?
我们:
- 可能从未用过
async/await - 不是微软专家
- 不是 C# 大师
async/await诚然,有时候我会忘记如何正确使用。- 可能得回谷歌一下,弄清楚为什么一个方法可以返回一个值,
Task但却不能使用async关键字。 - 可能会好奇为什么一个没有被标记为等待的方法
async可以被其调用者等待。
我希望这篇文章是写给“我们其他人”的,它切中要点,实用性强。
PS:如果您想更深入地了解这个话题,我建议您从这篇文章开始。
为什么选择异步?
使用有什么好处async/await?
面向网站开发人员
如果您使用 .Net 技术构建 Web 应用程序,答案很简单:可扩展性。
当您进行 I/O 调用(数据库查询、文件读取、从 HTTP 请求读取数据等)时,处理当前 HTTP 请求的线程只是在等待。
就是这样。它现在正等待操作系统返回结果。
例如,执行数据库查询最终会请求操作系统连接到数据库,发送消息并接收回复消息。但发出这些请求的是操作系统,而不是你的应用程序。
IO 操作需要时间。在这段时间里,等待线程(在你的应用程序中)可以用来做其他事情——特别是处理其他 HTTP 请求。
使用此功能可以让你的 .Net Web 应用程序在等待 IO 完成的同时
async/await处理更多 HTTP 请求。
面向桌面/应用程序开发人员
但是桌面应用程序无法处理HTTP请求……
桌面应用程序确实会处理用户输入(键盘、鼠标等)、动画等。而且只有一个 UI 线程来执行这些操作。
用户输入是用户输入
如果我们认为 Web 应用中的 HTTP 请求只是用户输入,桌面(或应用)键盘和鼠标事件也只是用户输入——那么对于桌面/应用开发者来说,情况实际上更糟!他们只有一个线程来处理用户输入!
IO 问题和修复方法仍然适用。
然而,CPU密集型任务的问题是另一个需要关注的方面。简而言之,这类任务不应该在UI线程上执行。
任务类型包括:
- 在循环中处理大量项目
- 计算用于报告的汇总数据
如果你的应用在主线程/UI线程上执行此操作,那么就没有任何东西可以处理用户输入和UI相关内容,例如动画等。
这会导致应用程序卡顿和延迟。
解决方案是将 CPU 密集型任务卸载到后台任务/线程。这涉及到如何为新线程/任务排队,如何ConfigureAwait(false)在非 UI 上下文中维护代码的异步分支等等。所有这些都超出了本文的讨论范围。
异步关键字
让我们开始研究async/await关键词以及它们的使用方法。
人们对 async 关键字存在误解。为什么呢?因为它看起来好像能让你的方法变成异步的。但实际上并非如此。
这真让人困惑。这个async关键字并不能使我的方法变为异步的吗?是的。
那它接下来会做什么?
这个关键字的作用仅仅async是启用 await 关键字。仅此而已。它没有其他任何作用。
所以,你就把async关键词看作关键词就好enableAwait。
等待关键词
关键字await是关键所在。它本质上是在告诉代码的读者:
我(这个线程)会确保如果这里发生异步操作,我会去做其他事情(比如处理 HTTP 请求)。将来某个线程会在异步操作完成后回到这里。
一般来说,最常见的用途await是在进行 I/O 操作时,例如从数据库查询中获取结果或从文件中获取内容。
当你的await方法执行 I/O 操作时,实际执行 I/O 操作的并不是你的应用程序,而是操作系统。所以你的线程只是在那里等待……
await它会指示当前线程停止运行,去做一些有用的事情。让操作系统和 .NET 框架稍后在需要时再分配另一个线程。
请将此图作为视觉指南:
var result1 = await SomeAsyncIO1(); // OS is doing IO while thread will go do something else.
// A thread gets the results.
var result2 = await SomeAsyncIO2(result1); // Thread goes to do something else.
// One comes back...
await SomeAsyncIO3(result2); // Goes away again...
// Comes back to finish the method.
此时你可能会问自己:
如果
async关键字不能使方法异步化,那么什么才能使方法异步化?
那么,是什么让一个方法成为异步方法呢?
嗯——正如我们所了解到的,这并不是async关键词。真是不可思议。
任何返回“可等待对象”的方法——Task或者Task<T>可以使用await关键字等待的方法。
实际上还有其他类型的可等待对象。而且,“可等待”的方法并不一定非得是异步方法。不过先别管这些——这部分内容是写给“我们其他人”看的。
就本文而言,我们将假设“异步方法”是指返回 trueTask或 false的方法Task<T>。
这种情况何时发生?
我们什么时候需要Task从方法中返回一个值呢?通常是在进行 I/O 操作时。大多数 I/O 库或 .NET 内置 API 都会提供方法的“异步”版本。
例如,该类SqlConnection有一个Open用于启动连接的方法。但是,它还有一个OpenAsync方法。它还有一个ExecuteNonQueryAsync方法。
public async Task<int> IssueSqlCommandAsync() {
using(var con = new SqlConnection(_connectionString))
{
// Some code to create an sql command "sqlCommand" would be here...
await con.OpenAsync();
return await sqlCommand.ExecuteNonQueryAsync();
}
}
使 ` OpenAsyncand`ExecuteNonQueryAsync方法异步的不是关键字async`a`,而是它们返回 `a`Task或 `b`Task<T>。
全程异步
可以这样做(注意缺少asyncand await):
public Task<int> GetSomeData() {
return DoSomethingAsync();
}
然后就是await这种方法:
// Inside some other method....
await GetSomeData();
GetSomeData它不会等待调用DoSomethingAsync,而是直接返回Task。请记住,它await并不关心方法是否使用了async关键字,它只要求方法返回一个Task。
这样做是可行的——创建一个调用异步方法但不等待的方法。
这是最佳实践
然而,这被认为是一种不良做法。为什么?
由于本文旨在简洁实用:
从头到尾使用 async/await 可以更好地捕获异步方法中的异常。
Task如果你用关键字标记每个返回 a 的方法async(这反过来又启用await关键字),它就能更好地处理异常,并且在查看异常消息和堆栈跟踪时使其更容易理解。
结论
简而言之:
-
该
async关键字并不会使方法异步——它只是启用该await关键字而已。 -
如果一个方法返回一个 `
Taskor`Task<T>,那么可以使用关键字await来管理我们代码的异步细节。 -
执行 I/O 操作总是会导致线程阻塞。这意味着您的 Web 应用程序无法并行处理足够多的 HTTP 请求,从而导致应用程序卡顿和延迟。
-
使用此功能
async/await可以帮助我们创建代码,使我们的线程在执行 I/O 操作时不再阻塞并执行有用的工作。 -
这使得 Web 应用程序每秒可以处理更多请求,并且对用户响应速度更快。
我希望这篇介绍能够让大家理解async/await。这并非一个简单的话题——而且一如既往——随着时间的推移,通过使用这项功能积累经验将帮助我们理解其运作原理。
关于这一点,还有很多话要说,还有很多概念需要探讨async/await。其中包括:
- 什么是
SynchronizationContext?我应该在什么情况下注意它? - .Net Core 和 .Net Framework 有什么区别?我应该注意哪些方面?
- 为什么我可以把一个方法标记为
async void?这样做有什么作用?我应该这样做吗? - 如何将 CPU 密集型任务卸载到后台线程/任务中?
- 是否可以在后台线程中执行任务,并在最后返回到 UI 线程?
- 如何在同步代码中调用带有
async关键字标记的方法?这样做会发生什么?
请告诉我你的想法——或者我是否遗漏了什么等等。谢谢!
保持联系
如何规划你的软件开发职业生涯
我会通过电子邮件简讯回答订阅者的问题,并就以下主题提供建议:
✔ 软件开发人员的职业发展阶段有哪些?
✔ 我如何知道自己处于哪个阶段?如何进入下一个阶段?
✔ 什么是技术领导者?如何成为一名技术领导者?
听起来很有趣?加入我们吧!
文章来源:https://dev.to/jamesmh/asyncawait-for-the-rest-of-us-c8a