• .Net Core中无处不在的Async/Await是如何提升性能的?


    一、简介

    Async/Await在.Net Core中真的是无处不在,到处都是异步操作,那为什么要用?有什么作用?别人说能提升性能?网上一堆文章看的绕晕了也没说清楚,

    所以这里从理论,实践,原理一个个解开这些疑问。

    二、Async/Await有什么用?

    1.Async/Await用法示例

    用法很简单,这里就不详细说具体怎么用了,只提供一个示例,我们的目标是研究它的作用。

     public class AsyncAwaitTest
        {
            public void Start()
            {
                Console.WriteLine($"aaa,线程Id:{Thread.CurrentThread.ManagedThreadId}");
                AsyncMethod();
                Console.WriteLine($"eee,线程Id:{Thread.CurrentThread.ManagedThreadId}");
                Console.ReadLine();
            }
            public async Task<bool> AsyncMethod()
            {
                Console.WriteLine($"bbb,线程Id:{Thread.CurrentThread.ManagedThreadId}");
                await Task.Run(() => {
    
                    Thread.Sleep(500);
                    Console.WriteLine($"ccc,线程Id:{Thread.CurrentThread.ManagedThreadId}");
                });
                Console.WriteLine($"ddd,线程Id:{Thread.CurrentThread.ManagedThreadId}");
                return true;
            }
        }

    2.async/await的特点

    1)不会阻塞线程

    从示例的执行顺序,可以看出,当执行async/await方法时,主线程遇到await关键字,主线程就返回执行“eee”,然后释放,而不是等待,新开了一个子线程6执行另外的业务,await前面的方法还是主线程执行,await后面的方法,等线程6执行完了再继续执行。

    2)同步的方式写异步

    虽然是用了异步,但还是等待执行结果再往下执行,执行流程是同步的。

    3.async/await能提升性能吗?

    这个应该是大家最关心的问题了。

    能提升单个请求的性能吗?

    答案是不能的。很明显看的到,await等待了结果再执行后面的逻辑,还是串行的,执行完该多少秒还是多少秒, 中间还切换线程去处理了,相比同步来说还多了切换线程的损耗。

    那async/await的意义何在?

    在于多请求并发处理,且资源有限的时候,能增加吞吐量(单位时间处理的请求),增加cpu的利用率。

    简单说就是有10个线程,每个线程的速度没有提升,然后居然QPS能提升?!

    先来看一段微软官网的描述

      此模型可很好地处理典型的服务器方案工作负荷。由于没有专用于阻止未完成任务的线程,因此服务器线程池可服务更多的Web请求。

      考虑使用两个服务器:一个运行异步代码,一个不运行异步代码。对于本例,每个服务器只有5个线程可用于服务器请求。此字数太小,不切实际,仅供演示。

      假设这两个服务器都接收6个并发请求。每个请求执行一个I/O操作。未运行异步代码的服务器必须对第6个请求排队,直到5个线程中的一个完成了I/O密集型工作

    并编写了响应。此时收到了第20个请求,由于队列过长,服务器可能会开始变慢。

      运行有异步代码的服务器也需要对第6个请求排队,但由于使用了async和await,I/O密集型工作开始时,每个线程都会得到释放,无需等到工作结束。

    收到第20个请求时,传入请求队列将变得很小(如果其中还有请求的话),且服务器不会慢。

      尽管这是一个人为想象的示例,但现实世界中其工作方式与此类似。事实上,相比服务器将线程专用于接收到的每个请求,使用async和await能够使

    服务器处理一个数量级的请求。

      注意上面官网描述的I/O密集型。什么样的是I/O密集型呢?,就是cpu性能比硬盘内存好太多,大部分时间都是cpu在等IO的读写操作。例如读文件,读文件的时候是不需要cpu参与的,只需要发一个命令给硬盘,硬盘读完文件会再通知cpu继续处理,这种叫DMA技术

      DMA(Direct Memory Access,直接存储器访问) 是所有现代电脑的重要特色,它是指一种高速的数据传输操作,允许在外部设备和存储器之间直接读写数据,既不通过cpu,也不需要cpu干预。

      这个时候异步就显出它的优势来了,比如读文件需要1s,如果是同步操作,那么就有一个线程在等1s再往下执行。如果是异步的,读文件的时候,这个线程就释放了,等读完文件,硬盘通知cpu再派一个线程接着处理,那中间的1秒,原来的线程就可以去处理其他请求了。

    4.代码对照说明

    public class HomeController : Controller
        {
            /// <summary>
            /// 同步请求
            /// </summary>
            /// <param name="path"></param>
            /// <returns></returns>
            public string GetData()
            {
                var result = System.IO.File.ReadAllBytes(@"F:\package\package.rar");
                return "ok";
            }
    
            /// <summary>
            /// 异步请求
            /// </summary>
            /// <param name="path"></param>
            /// <returns></returns>
            public async Task<string> GetDataAsync2()
            {
                var result = await System.IO.File.ReadAllBytesAsync(@"F:\package\package.rar");
                return "ok";
            }
        }

    同步请求的流程为

     可以看出,硬盘在读取文件时,线程是在等待的,这时候线程1在这1s中是不工作的,空等状态。

    异步请求的流程为

     异步请求时,线程1遇到await关键字,发出命令就返回,然后释放掉了,硬盘读完数据会通知cpu,这时cpu派一个新的线程去接着处理,

    因此,读文件的这1s,线程1可以去处理其它请求了,没有空等,这就是提高了cpu的利用率,单位时间内处理的请求数就变大了。

    cpu密集型的异步是不能提高QPS的,下面代码就是cpu密集型的。

    cpu密集型:计算密集型,硬盘、内存性能比cpu好很多,或不太需要访问I/O设备。

         /// <summary>
            /// 异步请求
            /// </summary>
            /// <param name="path"></param>
            /// <returns></returns>
            public async Task<string> GetDataAsync2()
            {
                await Task.Run(() => {
                    Thread.Sleep(100);//模拟业务处理耗时
                });
                return "ok";
            }

    这里前面主线程遇到await虽然释放了,但await里面又有一个线程接着工作,cpu(线程)并没有空闲

    I/O密集型中IO的操作有哪些呢?

    文件读写、http请求、数据库请求、redis请求。。。等等。

     开发中哪些推荐用异步呢?

    Web开发推荐、有Async的API的,Action、Filter、数据库访问、中间件等等。。。

  • 相关阅读:
    Authorize 示例
    javscript 实现iframe加载内容页出现Loading效果
    泛型的Distinct(IEqualityComparer)的用法
    关于ViewData与TempData
    将表 自增长列 清零 循环插入时间自增长
    linq count() sum() Min() Max() Average() 用法
    分页的存储过程
    将图片文件与文本文件合并成图片文件
    asp.net mvc利用Json验证数据和导向页面,解决重复提交问题
    JQUERY DIV浮动提示信息
  • 原文地址:https://www.cnblogs.com/wei325/p/15957951.html
Copyright © 2020-2023  润新知