前言

  1. 什么是async/await?

    await和async是.NET Framework4.5框架、C#5.0语法里面出现的技术,目的是用于简化异步编程模型。

  2. async和await的关系?

    async和await是成对出现的。

    async出现在方法的声明里,用于批注一个异步方法。光有async是没有意义的。

    await出现在方法内部,Task前面。只能在使用async关键字批注的方法中使用await关键字。

        private async Task DoSomething()
{
await Task.Delay(TimeSpan.FromSeconds(10));
}
  1. async/await会创建新的线程吗?

    不会。async/await关键字本身是不会创建新的线程的,但是被await的方法内部一般会创建新的线程。

  2. asp.net mvc/webapi action中使用async/await会提高请求的响应速度吗?

    不会。

正题

我们都知道web应用不同于winform、wpf等客户端应用,客户端应用为了保证UI渲染的一致性往往都是采用单线程模式,这个UI线程称为主线程,如果在主线程做耗时操作就会导致程序界面假死,所以客户端开发中使用多线程异步编程非常必要。

可web应用本身就是多线程模式,服务器会为每个请求分配工作线程。

既然async/await不能创建新线程,又不能使提高请求的响应速度,那.NET Web应用中为什么要使用async/await异步编程呢?

在 web 服务器上,.NET Framework 维护用于处理 ASP.NET 请求的线程池。 当请求到达时,将调度池中的线程以处理该请求。 如果以同步方式处理请求,则处理请求的线程将在处理请求时处于繁忙状态,并且该线程无法处理其他请求。

在启动时看到大量并发请求的 web 应用中,或具有突发负载(其中并发增长突然增加)时,使 web 服务调用异步会提高应用程序的响应能力。 异步请求与同步请求所需的处理时间相同。 如果请求发出需要两秒钟时间才能完成的 web 服务调用,则该请求将需要两秒钟,无论是同步执行还是异步执行。 但是,在异步调用期间,线程在等待第一个请求完成时不会被阻止响应其他请求。 因此,当有多个并发请求调用长时间运行的操作时,异步请求会阻止请求队列和线程池的增长。

下面用代码来实际测试一下:

  • 先是同步的方式,代码很简单,就是输出一下请求开始和结束的时间和线程ID:
        public ActionResult Index()
{
DateTime startTime = DateTime.Now;//进入DoSomething方法前的时间
var startThreadId = Thread.CurrentThread.ManagedThreadId;//进入DoSomething方法前的线程ID DoSomething();//耗时操作 DateTime endTime = DateTime.Now;//完成DoSomething方法的时间
var endThreadId = Thread.CurrentThread.ManagedThreadId;//完成DoSomething方法后的线程ID
return Content($"startTime:{ startTime.ToString("yyyy-MM-dd HH:mm:ss:fff") } startThreadId:{ startThreadId }<br/>endTime:{ endTime.ToString("yyyy-MM-dd HH:mm:ss:fff") } endThreadId:{ endThreadId }<br/><br/>");
} /// <summary>
/// 耗时操作
/// </summary>
/// <returns></returns>
private void DoSomething()
{
Thread.Sleep(10000);
}

使用浏览器开3个标签页进行测试(因为浏览器对同一域名下的连接数有限制,一般是6个左右,所以就弄3个吧):







可以看到耗时都是10秒,开始和结束的线程ID一致。

  • 下面改造成异步的:
        public async Task<ActionResult> Index()
{
DateTime startTime = DateTime.Now;//进入DoSomething方法前的时间
var startThreadId = Thread.CurrentThread.ManagedThreadId;//进入DoSomething方法前的线程ID await DoSomething();//耗时操作 DateTime endTime = DateTime.Now;//完成DoSomething方法的时间
var endThreadId = Thread.CurrentThread.ManagedThreadId;//完成DoSomething方法后的线程ID
return Content($"startTime:{ startTime.ToString("yyyy-MM-dd HH:mm:ss:fff") } startThreadId:{ startThreadId }<br/>endTime:{ endTime.ToString("yyyy-MM-dd HH:mm:ss:fff") } endThreadId:{ endThreadId }<br/><br/>");
} /// <summary>
/// 耗时操作
/// </summary>
/// <returns></returns>
private async Task DoSomething()
{
await Task.Run(() => Thread.Sleep(10000));
}

结果:







可以看到3次请求中,虽然耗时都是10秒,但是出现了开始和结束的线程ID不一致的情况,ID为22的这个线程工作了多次,这意味着使用异步方式在同一时间可以处理更多的请求

IIS默认队列长度:

await关键字不会阻塞线程直到任务完成。 它将方法的其余部分注册为任务的回调,并立即返回。 当await的任务最终完成时,它将调用该回调,并因此在其中断时继续执行方法。

简单来说:就是使用同步方法时,线程会被耗时操作一直占有,直到耗时操作完成。而使用异步方法,程序走到await关键字时会立即return,释放线程,余下的代码会放进一个回调中(Task.GetAwaiter()的UnsafeOnCompleted(Action)回调),耗时操作完成时才会回调执行,所以async/await是语法糖,其本质是一个状态机。

那是不是所有的action都要用async/await呢?

不是。一般的磁盘IO或者网络请求等耗时操作才考虑使用异步,不要为了异步而异步,异步也是需要消耗性能的,使用不合理会适得其反。

结论

async/await异步编程不能提升响应速度,但是可以提升响应能力(吞吐量)。异步和同步各有优劣,要合理选择,不要为了异步而异步。

.NET Web应用中为什么要使用async/await异步编程的更多相关文章

  1. C#中 Thread,Task,Async/Await 异步编程

    什么是异步 同步和异步主要用于修饰方法.当一个方法被调用时,调用者需要等待该方法执行完毕并返回才能继续执行,我们称这个方法是同步方法:当一个方法被调用时立即返回,并获取一个线程执行该方法内部的业务,调 ...

  2. 【转】C# Async/Await 异步编程中的最佳做法

    Async/Await 异步编程中的最佳做法 Stephen Cleary 近日来,涌现了许多关于 Microsoft .NET Framework 4.5 中新增了对 async 和 await 支 ...

  3. .NET 中的 async/await 异步编程

    原文出处: Teroy 的博客 前言 最近在学习Web Api框架的时候接触到了async/await,这个特性是.NET 4.5引入的,由于之前对于异步编程不是很了解,所以花费了一些时间学习一下相关 ...

  4. ASP.Net中的async+await异步编程

    在.NET Framework4.5框架.C#5.0语法中,通过async和await两个关键字,引入了一种新的基于任务的异步编程模型(TAP).在这种方式下,可以通过类似同步方式编写异步代码,极大简 ...

  5. async/await 异步编程(转载)

    转载地址:http://www.cnblogs.com/teroy/p/4015461.html 前言 最近在学习Web Api框架的时候接触到了async/await,这个特性是.NET 4.5引入 ...

  6. async/await 异步编程

    前言 最近在学习Web Api框架的时候接触到了async/await,这个特性是.NET 4.5引入的,由于之前对于异步编程不是很了解,所以花费了一些时间学习一下相关的知识,并整理成这篇博客,如果在 ...

  7. Async await 异步编程说明

    希望在编程上有些许提高所以 最近连续2篇博客都在说明多线程和异步编程的使用,异步和多线程之间区别请自行百度,因为理解不是特别透彻就不在叙述以免误导大家,这里写下新研究整理 task  和 await ...

  8. async & await 异步编程的一点巧方法

    await 关键字不会创建新的线程,而是由Task任务或是FCL中的xxxAsync等方法创建的线程,而且这里创建的线程都是基于线程池创建的工作线程,属于后台线程. await关键字会阻塞/暂停调用它 ...

  9. c# async/await异步编程死锁的问题

    在异步编程中,如果稍有不注意,就会造成死锁问题.何为死锁:即两个以上的线程同时争夺被互相锁住的资源,两个都不放手. 在UI或asp.net中,容易造成死锁的代码如下所示: private void b ...

随机推荐

  1. String,StringBuffer,StringBuilder三者的区别

    相同点: String,StringBuffer,StringBuilder,都是final类,不允许被继承,在本质上都是字符数组, 不同点: 1.String的长度是不可变的而后两者长度可变,在进行 ...

  2. 虚拟机安装配置(VMware与CentOS安装)

    VMware下载地址: https://www.vmware.com/cn/products/workstation-pro/workstation-pro-evaluation.html centO ...

  3. 你还不了解基于session的授权认证吗?

    前言 在漫长的开发过程中,权限认证是一个永恒不变的话题,随着技术的发展,从以前的基于sessionId的方式,变为如今的token方式.session常用于单体应用,后来由于微服务的兴起,分布式应用占 ...

  4. Java switch case语句

    switch case 语句判断一个变量与一系列值中某个值是否相等,每个值称为一个分支. switch case 语句语法格式如下: switch(expression){ case value : ...

  5. C语言基础知识(二)——二维数组

    二维数组本质 二维数组本质就是一维数组,只不过**形式是二维**,类似矩阵,使用二维数组表示会更加形象. 二维数组实例 float rain[5][12]; //内含5个数组元素的数组,每个数组元素内 ...

  6. 【图机器学习】cs224w Lecture 11 & 12 - 网络传播

    目录 Decision Based Model of Diffusion Large Cascades Extending the Model Probabilistic Spreading Mode ...

  7. day18 迭代器

    1,迭代器协议:对象必须提供一个next方法,执行该方法要么返回迭代中的下一项,要么就引起一个Stoplteration异常,只能往后走不能往前退: 2,可迭代对象:实现了迭代器协议的对象(如何实现: ...

  8. ES[7.6.x]学习笔记(十二)高亮 和 搜索建议

    ES当中大部分的内容都已经学习完了,今天呢算是对前面内容的查漏补缺,把ES中非常实用的功能整理一下,在以后的项目开发中,这些功能肯定是对你的项目加分的,我们来看看吧. 高亮 高亮在搜索功能中是十分重要 ...

  9. httpclient介绍与请求方式详解

    httpClient工具介绍 HTTP协议可能是现在lntemet上使用得最多.最重要的协议了,越来越多的Java应用程序需要直接通过HTTP协议来访问网络资源.虽然在JDK的java.net包中已经 ...

  10. Spring_Bean的配置方式

    1.通过工厂方法配置bean beans-factory.xml <?xml version="1.0" encoding="UTF-8"?> &l ...