dotnet 开发 API 的时候使用 asyn 、await 有什么优势吗 - V2EX
V2EX = way to explore
V2EX 是一个关于分享和探索的地方
现在注册
已注册用户请  登录
请不要在回答技术问题时复制粘贴 AI 生成的内容
daijinming
V2EX    程序员

dotnet 开发 API 的时候使用 asyn 、await 有什么优势吗

  •  
      daijinming 2019-11-06 15:44:22 +08:00 4174 次点击
    这是一个创建于 2165 天前的主题,其中的信息可能已经有所发展或是发生改变。

    如果一个 API 被标记为 asyn,和 没有 asyn 的 api 有什么区别?不是太理解,这个和多线程有关系吗,求教

    29 条回复    2019-11-08 15:05:31 +08:00
    darkalien
        1
    darkalien  
       2019-11-06 15:54:44 +08:00
    darkalien
        2
    darkalien  
       2019-11-06 16:02:27 +08:00
    关于 async await 的理解,另推荐自己的文章:异步编程关键字 Async 和 Await - https://www.cnblogs.com/AlienXu/p/9529541.html
    hihipp
        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
    loqixh
        4
    loqixh  
       2019-11-06 16:27:04 +08:00
    await 本质就是通过编译器变化的无栈协程, 和 go 的协程是一样的, 其它的说法都是往复杂里扯
    secondwtq
        5
    secondwtq  
       2019-11-06 19:05:33 +08:00
    @loqixh Go 是 ”stackful“ 的吧 ...
    ”stackless“ 的有 Javascript,C++,Kotlin,一大堆 ...
    loqixh
        6
    loqixh  
       2019-11-06 19:11:18 +08:00
    @secondwtq 对 但是效果一样, 只是实现方法的区别
    tinkerer
        7
    tinkerer  
       2019-11-06 19:22:51 +08:00
    tinkerer
        8
    tinkerer  
       2019-11-06 19:23:54 +08:00
    sorry, 误操作, 十分抱歉, 上一条与此贴无关
    seakingii
        9
    seakingii  
       2019-11-06 21:28:14 +08:00   1
    你可以理解为在 await 的地方中断执行,在其它线程执行,执行完成后再返回 await 处,继续执行后面的代码.

    和没有 async,await 的版本相比,就是提高了系统的并发量
    seakingii
        10
    seakingii  
       2019-11-06 21:29:30 +08:00
    另外以前是没有 async,await 机制的,要写类似的代码,需要自己写线程,或者利用线程池什么的.代码和现在比起来复杂的多.
    Presbyter
        11
    Presbyter  
       2019-11-06 21:47:47 +08:00
    编译器的语法糖,可以更方便的使用线程池进行多线程应用开发.
    MonoLogueChi
        12
    MonoLogueChi  
       2019-11-06 23:51:20 +08:00 via Android   1
    这个是异步编程,可以减少阻塞。另外 async 只是标记这个方法里可能会出现 await,在编译时没有实际作用,你也可以标记 async 而不写 await,这样编译也能过,只是 IDE 会提示你不要这样写。
    lbp0200
        13
    lbp0200  
       2019-11-07 00:21:35 +08:00 via iPhone
    关键字放在这里,让人感觉怪怪的
    luozic
        14
    luozic  
       2019-11-07 02:45:34 +08:00 via iPhone
    核心是现在大部分的应用是 IO 密集,实际就是 curd 密集型应用;
    xuanbg
        15
    xuanbg  
       2019-11-07 09:11:23 +08:00
    异步模式的好处就是可以同时进行多项任务,减少任务的总时间。就像做饭一样,没人会等电饭煲做好饭再开始炒菜。都是做饭(async)、炒菜(async)并行的,最后饭和菜都好了(await),就能吃了。
    xingheng
        16
    xingheng  
       2019-11-07 11:12:51 +08:00
    @xuanbg 这个好。但是如果有客人(request)就点了一个蛋炒饭(async),这样的话我觉得 sync 版的蛋烧饭会比 async 版的蛋炒饭更快,因为没有切换线程的损耗了。

    关于楼上提到的并发量提高的问题我有些质疑,一个 request 在发起到完成之前,这个 working thread 应该是一直存在的,并不会因为 async 的使用导致 working thread dispose 或者 reuse 的情况。

    我的理解对吗?
    crclz
        17
    crclz  
       2019-11-07 12:57:05 +08:00
    @xingheng 这个 working thread 不能说时一直存在的。
    假如我调用 await GetDishAsync(),那么 GetDishAsync 任务所用到的线程会被高效管理。
    好了,现在问题就来了:GetDishAsync 的 caller 所在的线程(你提到的 working thread )是否会一直存在?
    不是的。因为 await GetDishAsync()的 caller 一定是一个 async 方法,它的线程资源一样会被高效管理。
    xingheng
        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 设计层面上我觉得只是相对提高了响应请求处理的单位时间,勉强上可以说是间接地提高了并发量。

    请指正。
    xuanbg
        19
    xuanbg  
       2019-11-07 14:19:40 +08:00
    @xingheng 并发量是会提高的。系统在同一时间能处理的 request 数量是和 CPU 的核数是一致的。那么,每个 request 的处理时间越短,“单位时间”内能处理的 request 数量就越多。请注意,我们正常情况下说的并发量,都是指「单位时间内的 request 处理数量」。
    xuanbg
        20
    xuanbg  
       2019-11-07 14:26:25 +08:00
    @xingheng 上面的说法可能还不是太准确。事实上在 CPU 满负荷的情况下,异步并不一定能够提高效率。甚至有可能因为线程切换而导致效率下降。而正常情况下,CPU 负荷是需要控制在 60%以下的。这个时候,异步基本上都能提升处理效率。当然,CPU 的也负载会比同步更高一些。
    crclz
        21
    crclz  
       2019-11-08 00:52:03 +08:00
    回复测试
    crclz
        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=貌似是有效磁盘数量。很久以前读的文章,记不太清。
    httpsgithub.combrettwooldridge/HikariCP/wiki/About-Pool-Sizing
    crclz
        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 的性能。
    crclz
        24
    crclz  
       2019-11-08 01:01:13 +08:00
    @xingheng @xuanbg 回复一直发不出来,截断后发出来了。
    loqixh
        25
    loqixh  
       2019-11-08 09:53:31 +08:00
    @crclz 吐血, asp.net core 一直超过 go 的网络性能啊........虽然比不上 java 的 netty 但是 Netty 用得人不多, 所以日常来说.net core 性能基本是最高之一
    crclz
        26
    crclz  
       2019-11-08 10:06:41 +08:00
    @loqixh 网上我看过一些评测,发现都是 go (的某个框架)优于.net core
    loqixh
        27
    loqixh  
       2019-11-08 10:18:01 +08:00
    @crclz 因为都是用玩具框架对比.net core 的全功能框架,就算这样 go 的性能也就比.net core 高 10%-20%的样子, 真正实用性能可以看 aws lambda 的性能测试.net core 是其中最高的
    xingheng
        28
    xingheng  
       2019-11-08 14:29:16 +08:00
    @xuanbg 你说得对,是我上面对“并发量”的描述不对,应该算 keepalive connection/request 比较准确。
    xingheng
        29
    xingheng  
       2019-11-08 15:05:31 +08:00
    @crclz 学习了。我单独去查了一下“effective_spindle_count”的几篇文章,你说的 connection pool 大小是指的数据库层面的,不是 http/tcp connection 的。我觉得跟题主说的 API 层面上的 async 不太对等。
    关于     帮助文档     自助推广系统     博客     API     FAQ     Solana     1018 人在线   最高记录 6679       Select Language
    创意工作者们的社区
    World is powered by solitude
    VERSION: 3.9.8.5 25ms UTC 18:34 PVG 02:34 LAX 11:34 JFK 14:34
    Do have faith in what you're doing.
    ubao snddm index pchome yahoo rakuten mypaper meadowduck bidyahoo youbao zxmzxm asda bnvcg cvbfg dfscv mmhjk xxddc yybgb zznbn ccubao uaitu acv GXCV ET GDG YH FG BCVB FJFH CBRE CBC GDG ET54 WRWR RWER WREW WRWER RWER SDG EW SF DSFSF fbbs ubao fhd dfg ewr dg df ewwr ewwr et ruyut utut dfg fgd gdfgt etg dfgt dfgd ert4 gd fgg wr 235 wer3 we vsdf sdf gdf ert xcv sdf rwer hfd dfg cvb rwf afb dfh jgh bmn lgh rty gfds cxv xcv xcs vdas fdf fgd cv sdf tert sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf shasha9178 shasha9178 shasha9178 shasha9178 shasha9178 liflif2 liflif2 liflif2 liflif2 liflif2 liblib3 liblib3 liblib3 liblib3 liblib3 zhazha444 zhazha444 zhazha444 zhazha444 zhazha444 dende5 dende denden denden2 denden21 fenfen9 fenf619 fen619 fenfe9 fe619 sdf sdf sdf sdf sdf zhazh90 zhazh0 zhaa50 zha90 zh590 zho zhoz zhozh zhozho zhozho2 lislis lls95 lili95 lils5 liss9 sdf0ty987 sdft876 sdft9876 sdf09876 sd0t9876 sdf0ty98 sdf0976 sdf0ty986 sdf0ty96 sdf0t76 sdf0876 df0ty98 sf0t876 sd0ty76 sdy76 sdf76 sdf0t76 sdf0ty9 sdf0ty98 sdf0ty987 sdf0ty98 sdf6676 sdf876 sd876 sd876 sdf6 sdf6 sdf9876 sdf0t sdf06 sdf0ty9776 sdf0ty9776 sdf0ty76 sdf8876 sdf0t sd6 sdf06 s688876 sd688 sdf86