如果一个 API 被标记为 asyn,和 没有 asyn 的 api 有什么区别?不是太理解,这个和多线程有关系吗,求教
1
darkalien 2019-11-06 15:54:44 +08:00
|
2
darkalien 2019-11-06 16:02:27 +08:00
关于 async await 的理解,另推荐自己的文章:异步编程关键字 Async 和 Await - https://www.cnblogs.com/AlienXu/p/9529541.html
|
3
hihipp 2019-11-06 16:16:10 +08:00
服务器上的 async 有一篇官方文章说过,很久了,知识还是可用的,是从线程池说起的。
英文原文: https://msdn.microsoft.com/en-us/magazine/dn802603.aspx?f=255&MSPPError=-2147217396 中文: https://msdn.microsoft.com/zh-cn/magazine/dn802603.aspx?f=255&MSPPError=-2147217396 |
4
loqixh 2019-11-06 16:27:04 +08:00
await 本质就是通过编译器变化的无栈协程, 和 go 的协程是一样的, 其它的说法都是往复杂里扯
|
5
secondwtq 2019-11-06 19:05:33 +08:00
@loqixh Go 是 ”stackful“ 的吧 ...
”stackless“ 的有 JavaScript,C++,Kotlin,一大堆 ... |
7
tinkerer 2019-11-06 19:22:51 +08:00
|
8
tinkerer 2019-11-06 19:23:54 +08:00
sorry, 误操作, 十分抱歉, 上一条与此贴无关
|
9
seakingii 2019-11-06 21:28:14 +08:00 1
你可以理解为在 await 的地方中断执行,在其它线程执行,执行完成后再返回 await 处,继续执行后面的代码.
和没有 async,await 的版本相比,就是提高了系统的并发量 |
10
seakingii 2019-11-06 21:29:30 +08:00
另外以前是没有 async,await 机制的,要写类似的代码,需要自己写线程,或者利用线程池什么的.代码和现在比起来复杂的多.
|
11
Presbyter 2019-11-06 21:47:47 +08:00
编译器的语法糖,可以更方便的使用线程池进行多线程应用开发.
|
12
MonoLogueChi 2019-11-06 23:51:20 +08:00 via Android 1
这个是异步编程,可以减少阻塞。另外 async 只是标记这个方法里可能会出现 await,在编译时没有实际作用,你也可以标记 async 而不写 await,这样编译也能过,只是 IDE 会提示你不要这样写。
|
13
lbp0200 2019-11-07 00:21:35 +08:00 via iPhone
关键字放在这里,让人感觉怪怪的
|
14
luozic 2019-11-07 02:45:34 +08:00 via iPhone
核心是现在大部分的应用是 IO 密集,实际就是 curd 密集型应用;
|
15
xuanbg 2019-11-07 09:11:23 +08:00
异步模式的好处就是可以同时进行多项任务,减少任务的总时间。就像做饭一样,没人会等电饭煲做好饭再开始炒菜。都是做饭(async)、炒菜(async)并行的,最后饭和菜都好了(await),就能吃了。
|
16
xingheng 2019-11-07 11:12:51 +08:00
@xuanbg 这个🌰好。但是如果有客人(request)就点了一个蛋炒饭(async),这样的话我觉得 sync 版的蛋烧饭会比 async 版的蛋炒饭更快,因为没有切换线程的损耗了。
关于楼上提到的并发量提高的问题我有些质疑,一个 request 在发起到完成之前,这个 working thread 应该是一直存在的,并不会因为 async 的使用导致 working thread dispose 或者 reuse 的情况。 我的理解对吗? |
17
crclz 2019-11-07 12:57:05 +08:00
@xingheng 这个 working thread 不能说时一直存在的。
假如我调用 await GetDishAsync(),那么 GetDishAsync 任务所用到的线程会被高效管理。 好了,现在问题就来了:GetDishAsync 的 caller 所在的线程(你提到的 working thread )是否会一直存在? 不是的。因为 await GetDishAsync()的 caller 一定是一个 async 方法,它的线程资源一样会被高效管理。 |
18
xingheng 2019-11-07 13:30:50 +08:00
@crclz 有理。那我们再往下(main thread)追溯,(我没有写过 c#服务端,从其他语言推断的),main thread 或者说函数入口一定是 sync,每一个从 port 过来的请求一定是在 c#服务端对应一个 working thread 的,高效管理是语言级的特性,但是 working thread 的数量一定是不会减少的,working thread 之间(对外)也没有依赖关系,所以并发量还是没有增大。
我上面的蛋炒饭(async)假定确实很 critical,全程只有一个 async task 的需求是非常少见的,async 在这种情况下应该是没有什么好处的,高效管理只是说损耗很小,但不是没有。 async 在设计层面上是成功的,也推荐使用。在 API 设计层面上我觉得只是相对提高了响应请求处理的单位时间,勉强上可以说是间接地提高了并发量。 请指正。 |
19
xuanbg 2019-11-07 14:19:40 +08:00
@xingheng 并发量是会提高的。系统在同一时间能处理的 request 数量是和 CPU 的核数是一致的。那么,每个 request 的处理时间越短,“单位时间”内能处理的 request 数量就越多。请注意,我们正常情况下说的并发量,都是指「单位时间内的 request 处理数量」。
|
20
xuanbg 2019-11-07 14:26:25 +08:00
@xingheng 上面的说法可能还不是太准确。事实上,在 CPU 满负荷的情况下,异步并不一定能够提高效率。甚至有可能因为线程切换而导致效率下降。而正常情况下,CPU 负荷是需要控制在 60%以下的。这个时候,异步基本上都能提升处理效率。当然,CPU 的也负载会比同步更高一些。
|
21
crclz 2019-11-08 00:52:03 +08:00
回复测试
|
22
crclz 2019-11-08 00:59:31 +08:00
@xingheng 同步方法可以直接执行 DoSomethingAsync(),不用 await。所以就只会存在一个线程(或者几个)不断地接受请求、将请求传至 pipeline,经过各种中间件。从第一个中间件开始,就已经是 async 方法。另外我推测应该是直接调用的 DoSomethingAsync(),不加 await。我再在下文说一下何为“高效管理”。
@xuanbg 并发量和 cpu 核心数的关系粗略的来说是呈线性关系,但具体的下文讲。 先从数据库连接池的最佳大小说起。 连接池的最佳大小:connections = (core_count * 2) + effective_spindle_count。core_count:核心数; effective_spindle_count=貌似是有效磁盘数量。很久以前读的文章,记不太清。 https🐎github.com🐎brettwooldridge/HikariCP/wiki/About-Pool-Sizing |
23
crclz 2019-11-08 00:59:44 +08:00
这篇文章中貌似还说,其实所有类似的(例如线程池,选择最佳的线程池大小),也基本适用于这个公式。
也就是说,线程池的大小有一个最优的大小。在这个最优的大小下,你的吞吐量、响应时间最优。 这个最优的大小由 C#管理,程序员不用操心。 而 C#的 Task 是什么? Task 可以简单的理解为“任务”。你不用关心这些“任务”是如何完成的,你只需要知道:可以用 DoSomethingAsync()来开启一个 Task (并获得该 Task 实例)、可以用 await SomeTask 来等待 Task 结束。至于如何完成 Task 的任务,则是 c#的事情——c#会高效利用线程池里的资源,来完成“任务”:在 await 一个阻塞性的 task 时,没有线程会被拿来等待(这也是传统多线程方式无法企及的)。一个 Task 可能会先后被 1 个、2 个、5 个或者多个 worker thread 接手,但这些东西开发者都不需要关心。又由于线程池里 worker thread 的数量设计很合理,所以这种方法能达到最优的表现。 唯一的缺点就是,要严格控制代码(或许 VS 的各种 analyzer 包能容易的做到这一点),不能出现传统的同步阻塞式方法 例如 File.Read();而是要将它们全都换成 await File.ReadAsync()这种。因为线程池的 worker thread 资源是很宝贵的:核心*2+硬盘数,在 4 核 cpu 下,这个值仅仅为 9。c#可能有一套更优的公式,但也离这个值不远。一旦 worker thread 中运行了同步阻塞式代码,宝贵的线程池资源会被浪费。可能造成 threadpool exhaustion 等问题(这个我不是很懂),但至少会让吞吐量下降到 线程池大小 /响应时间。 相比来说,感觉 go 的并发代码编写方式、内部实现都很可以。希望 .Net Core 早日达到 go 的性能。 |
25
loqixh 2019-11-08 09:53:31 +08:00
@crclz 吐血, asp.net core 一直超过 go 的网络性能啊........虽然比不上 java 的 netty 但是 Netty 用得人不多, 所以日常来说.net core 性能基本是最高之一
|
27
loqixh 2019-11-08 10:18:01 +08:00
@crclz 因为都是用玩具框架对比.net core 的全功能框架,就算这样 go 的性能也就比.net core 高 10%-20%的样子, 真正实用性能可以看 aws lambda 的性能测试.net core 是其中最高的
|