返回 async/await 异步编程

关于异步的常见问题

以下为完整正文内容。

正文

大部分源自个人学习时产生过的疑问,少部分来自网络。 1:异步就是为了解放UI线程吗? 不只是。解放 UI 线程只是最明显的 UI 程序:解放 UI 线程,让界面不卡 Web 服务端:解放工作线程,让服务器扛住更多并发 桌面应用:同时解放 UI 和后台 I/O 线程 移动端:省电省资源 异步的本质:在任何需要“等待”的场景下,把原本等待而不干活的假繁忙线程资源回收利用。 2:异步等待期间发生了什么? 一个任务的生命周期分三段: 干活阶段(有线程):线程执行你的代码,发出指令(如数据库查询) 等待阶段(无线程):操作系统在监控,没有任何 .NET 线程被占用 结果返回阶段(又分配线程):操作系统通知完成,线程池分配一个新线程继续执行 3:大量并发访问时,异步怎么处理? 同步:10000 个请求需要 10000 个线程同时等着,内存撑爆 异步:等待期间线程被释放,去接新请求。10000 个请求只需要几十个线程来回穿插。请求和返回不绑定同一个线程,资源持续流转 4:那耗时的事都是谁在做? 数据库查询 → 数据库服务器 文件读写 → 操作系统内核 网络请求 → 网卡 + 对方服务器 定时等待 → 操作系统时钟 耗时的事情由数据库、磁盘、网卡、操作系统这些基础设施完成。.NET 线程只负责发指令和收结果,中间等待期不参与。 5:线程到底是什么定位? 线程是 CPU 的代办员。 它不干活,活是 CPU 干的。线程只是代表你的代码进入 CPU 执行队列,保存执行进度,让你代码能跑起来。 一个线程约 1MB 内存,多了就吃内存。 异步中的线程:只做指令的下发和接收,传完话就下班。厨房做菜的时候传话的去接别的单,菜好了铃一响再端回来。只干传话的活,不干厨房的事。 线程本身接请求、发指令非常快,真正费时间的是等待。 等数据库、等网络、等磁盘。 异步把“等”这个环节从线程身上卸下来,交给操作系统,线程只负责传话。 所以少量线程就能扛住海量并发,这是异步真正的价值所在。 6.到底什么时候该用异步 不是所有方法都得加 async。如果方法里只做纯计算,没有 I/O 操作(文件、数据库、网络),就不需要异步。需要异步的标志是:方法里要调一个名字带 Async 的方法。 7. Task.Run 和 async/await 有什么区别? Task.Run 是把一段代码扔到后台线程去跑(比如密集计算),主线程不卡。async/await 是处理 I/O 等待的,等待期间不占线程。两者解决不同问题。 8. “ConfigureAwait(false) 是什么,要不要写?” 它告诉 await:任务完成后不用回到原线程(比如 UI 线程),随便哪个线程都行。在类库代码里写它可以避免死锁,也能微提性能。UI 代码和 ASP.NET Core 控制器里一般不用写。 9. “async void 为什么危险,只能用在哪儿?” async void 方法出了异常不会被捕获,会直接崩掉整个程序。 它只能用在事件处理函数(比如按钮点击),其他所有地方必须用 async Task。 初学就当成没有这个搭配就行,等实际有需要再去了解 10. 异步方法里能没有 await 吗? 能,但编译器会警告。 没有 await 的 async 方法会同步跑完,白加一个状态机开销,没意义 11. 多个异步任务怎么同时跑?WhenAll 和 WhenAny 怎么用? 当你需要同时下载几个网页,而不是一个一个下载。 Task.WhenAll 同时启动所有任务,等全部完成; Task.WhenAny 等第一个完成的就继续。这是异步提速的常用手段。 12.async/await 是多线程吗? 不是。异步是“不占线程等”,不是开新线程。线程把活交给操作系统就跑了,等操作系统干完再通知线程回来。 13. await 会卡住线程吗? 不会。await 的时候线程被释放了,可以去处理其他请求。 14.顺序 await 两个任务,第二个什么时候开始? 第一个 await 等完了第二个才开始。如果想两个同时进行,先把 Task 存起来再统一 await,或者用 Task.WhenAll。 15.调异步方法和 await 的区别? 调方法的那一刻操作就启动了,跟有没有 await 没关系。await 只是等着拿结果。 16.async void 和 async Task 有什么区别? async void 的异常无法被捕获,调用者也完全不知道它什么时候执行完。async Task 可以正常 await 和捕获异常。 17.为什么 async void 方法执行完之前代码就往下走了? 因为调用者不能 await 它,所以后面的代码在任务完成前就执行了。