重新理解:ASP.NET 异步编程(转)

时间:2022-05-14 04:44:59

http://www.cnblogs.com/xishuai/p/asp-net-async-await-and-exception-handling.html

相关博文:

本来这篇博文想探讨下异步中的异常操作,但自己在做异步测试的时候,又对 ASP.NET 异步有了新的认识,可以说自己之前对异步的理解还是有些问题,先列一下这篇博文的三个解惑点:

  • async await 到底是什么鬼???
  • 异步操作中发生异常,该如何处理?
  • 异步操作中发生异常(有无 catch throw 情况),Application_Error 会不会捕获?

之前测试过异步中的同步(很多种情况),这次我们把测试代码写更复杂些(异步中再进行异步),代码如下:

[Route("")]
[HttpGet]
public async Task<string> Index()
{
System.Diagnostics.Debug.WriteLine("Thread.CurrentThread.ManagedThreadId1:" + Thread.CurrentThread.ManagedThreadId);
var result = await Test();
System.Diagnostics.Debug.WriteLine("Thread.CurrentThread.ManagedThreadId6:" + Thread.CurrentThread.ManagedThreadId);
return result;
} public static async Task<string> Test()
{
System.Diagnostics.Debug.WriteLine("Thread.CurrentThread.ManagedThreadId2:" + Thread.CurrentThread.ManagedThreadId);
using (var client = new HttpClient())
{
var response = await client.GetAsync("http://*.com/questions/14996529/why-is-my-async-asp-net-web-api-controller-blocking-the-main-thread");
await Test2();
System.Diagnostics.Debug.WriteLine("Thread.CurrentThread.ManagedThreadId5:" + Thread.CurrentThread.ManagedThreadId);
return await response.Content.ReadAsStringAsync();
}
} public static async Task<string> Test2()
{
System.Diagnostics.Debug.WriteLine("Thread.CurrentThread.ManagedThreadId3:" + Thread.CurrentThread.ManagedThreadId);
using (var client = new HttpClient())
{
var response = await client.GetAsync("http://*.com/questions/33408905/pgadminiii-bug-on-query-tool");
System.Diagnostics.Debug.WriteLine("Thread.CurrentThread.ManagedThreadId4:" + Thread.CurrentThread.ManagedThreadId);
return await response.Content.ReadAsStringAsync();
}
}

输出结果(执行四次):

Thread.CurrentThread.ManagedThreadId1:8
Thread.CurrentThread.ManagedThreadId2:8
Thread.CurrentThread.ManagedThreadId3:6
Thread.CurrentThread.ManagedThreadId4:6
Thread.CurrentThread.ManagedThreadId5:6
Thread.CurrentThread.ManagedThreadId6:6 Thread.CurrentThread.ManagedThreadId1:7
Thread.CurrentThread.ManagedThreadId2:7
Thread.CurrentThread.ManagedThreadId3:8
Thread.CurrentThread.ManagedThreadId4:7
Thread.CurrentThread.ManagedThreadId5:7
Thread.CurrentThread.ManagedThreadId6:7 Thread.CurrentThread.ManagedThreadId1:5
Thread.CurrentThread.ManagedThreadId2:5
Thread.CurrentThread.ManagedThreadId3:5
Thread.CurrentThread.ManagedThreadId4:6
Thread.CurrentThread.ManagedThreadId5:6
Thread.CurrentThread.ManagedThreadId6:6 Thread.CurrentThread.ManagedThreadId1:8
Thread.CurrentThread.ManagedThreadId2:8
Thread.CurrentThread.ManagedThreadId3:8
Thread.CurrentThread.ManagedThreadId4:8
Thread.CurrentThread.ManagedThreadId5:8
Thread.CurrentThread.ManagedThreadId6:8

这个测试方法,我执行了无数次,大致就是上面的四种情况,我当时看到输出结果,其实是很凌乱的,我也大家也一样,并心里有一些疑问:你这真是异步编程吗?为啥线程千奇百怪?并且最后那个还只有一个线程,这和同步有啥区别???

针对上面这个疑问,我想了很久,并对自己产生了一些质疑的声音:你每天都在写 async await 代码,你真的了解它吗???然后我又重新找到上面那篇 jesse liu 的博文,反复读了很多篇,最后终于有了一些“顿悟”,结合上面的测试代码,我大致画了一张示意图:

重新理解:ASP.NET 异步编程(转)

结合上面的图,我说一下自己的理解,在做测试的时候,HttpClient.GetAsync 尽量让它执行时间长些,比如请求的 URL 可以是 * 或 github(原因你懂得!),因为有个时间差,这样我们可以更好的了解线程的执行情况,上面图中“线程1、线程1x、线程3x、线程4x”等等,这些并不是不同线程,也就是说线程1有可能等于线程1x或线程3x。。。从上面的输出结果就可以看出,用线程x来表示两个输出之间所经历的 await 次数,这就证明了一个疑惑:await 并不一定会创建和之前不一样的线程。

到底什么是异步???我个人觉得,async 异步是一个伪概念,await 等待才是精髓,一个线程可以响应多个请求,如果是同步编程,一个线程在处理某一个请求的时候阻塞了(比如上面测试代码中的 HttpClient.GetAsync 网络操作),那么这个线程就会一直等待它处理,在这个等待的过程中,那么其他请求就不能再使用这个线程,又因为 IIS 线程池中的线程数量有限,那么同步编程下,高并发将是一个头疼的问题,试想一下,如果线程池中的线程数量为 100 个,这 100 个线程在同时处理 100 个请求的时候,都悲催的阻塞掉了,这时候第 101 个请求将无法执行,那么并发量就是 100。

接上面,同样的处理过程,如果是异步编程,那将是什么情况呢?比如一个线程在处理某一个请求的时候,执行到 await 操作,那么这个线程将会释放回到线程池,然后进行等待,等待的过程中,原来的那个线程就可以处理其他请求或者这个请求的其他操作,注意等待并不是线程等待,而是操作等待,我原来就很不理解这个地方,如果是线程等待,就表示这个线程会一直等待它完成,那和同步编程就是一样的了,所以这种理解是错误的,你可以这样理解:await 等待的过程中,没有线程!!!

再接上面,等待操作完成之后,这时候就会从线程池中随机拿一个线程继续执行,拿到的这个线程有可能是 await 操作刚刚释放掉的,但也有可能是其他线程,上图中的 2-6 操作就是这样,一图胜千言:

重新理解:ASP.NET 异步编程(转)

了解了整个过程之后,你才会明白 async await 到底是什么鬼?以及它真正的用武之地是什么?简单总结几点内容:

  • async 异步网络处理作用最明显(HttpClient 请求或数据库连接):这个我们大家都很清楚,也很好理解,如果是其他操作,比如一个异步方法中你做了很多费时的计算,那这个异步将没什么效果,说白了和同步一样,而对于网络操作,我们一般不做处理,发起请求之后等待它完成就行,所以这时候执行到这的线程,可以释放并会到线程池中,网络操作执行完成之后,再从线程池中随机拿一个线程继续执行。
  • async 异步并不是真正意义上的“异步”:什么意思呢?你仔细看下上面测试的输出结果,会发现 ManagedThreadId1-6 是顺序输出的,而不是先输出 ManagedThreadId4 再输出 ManagedThreadId3,所以,异步和同步的执行过程是一样的,并且一个请求下,执行时间也是一样的,上面的异步测试其实某种意义上,是测试不出任何东西的(从测试结果就可以看出),异步并不能减少你的执行时间,而是增加你的请求执行数量,这个东西说白了,其实就是并发量。
  • async 异步的精髓是 await:这个之前已经提到了,准确来说,async 异步的精髓是 await 时的线程回收与完成之后的线程切换,这个操作最大的价值是,避免线程的浪费等待,充分利用线程的执行,有点类似于地主不能容忍奴隶闲着做无意义的事,而是希望他们 24 小时不停工作一样。

另外,在 ASP.NET 应用程序中,我们可以使用 Thread.CurrentThread 来访问当前的执行线程,我之前想做这样一个测试,让当前执行线程 Sleep 一段时间,看看其他线程会不会执行,但 Thread.CurrentThread 并没有 Sleep 方法,而必须这样访问 Thread.Sleep(int millisecondsTimeout),如果这样执行这段代码,那么当前线程将会 Sleep,但其他线程并不会在它 Sleep 的时候,而继续执行,为什么?因为 CPU 在同一时间段内只能执行一个线程。

了解了 async await 到底是什么鬼后,博文一开始剩下的两个有关异步操作中的异常问题,现在理解起来就非常容易了:

  • 异步操作中发生异常,该如何处理?:和同步一样处理,同步中报错,异步也一样报错,有人可能有这样的疑问,比如测试代码中的 Index Action,执行到 await Test 内部操作的时候,突然抛出异常了,然后就想当然的认为,既然是异步执行的 Test 方法,那 Index 应该不会影响吧?其实你执行之后就会发现,Index 页面还是会抛出异常的,所以异常和异步没半毛钱关系
  • 异步操作中发生异常(有无 catch throw 情况),Application_Error 会不会捕获?:无 catch,Application_Error 会捕获;有 catch 无 throw,Application_Error 不会捕获;有 catch 有 throw,Application_Error 会捕获。

如果我们想让某一个异步方法,在执行抛出异常的时候,而不影响其他异步方法,那我们就 catch 而不 throw,比如我们的测试代码:

[Route("")]
[HttpGet]
public async Task<string> Index()
{
System.Diagnostics.Debug.WriteLine("Thread.CurrentThread.ManagedThreadId1:" + Thread.CurrentThread.ManagedThreadId);
var result = await Test();
System.Diagnostics.Debug.WriteLine("Thread.CurrentThread.ManagedThreadId6:" + Thread.CurrentThread.ManagedThreadId);
return result;
} public static async Task<string> Test()
{
try
{
System.Diagnostics.Debug.WriteLine("Thread.CurrentThread.ManagedThreadId2:" + Thread.CurrentThread.ManagedThreadId);
using (var client = new HttpClient())
{
var response = await client.GetAsync("http://*.com/questions/33408905/pgadminiii-bug-on-query-tool");
System.Diagnostics.Debug.WriteLine("Thread.CurrentThread.ManagedThreadId3:" + Thread.CurrentThread.ManagedThreadId);
throw new Exception("test exception");//这里出现了异常
return await response.Content.ReadAsStringAsync();
}
}
catch (Exception ex)
{
System.Diagnostics.Debug.WriteLine("异常信息:" + ex.Message);
return "";
//throw ex;
}
}

这样的效果就是 Index 页面不会报错,并且也不会影响其他方法执行,现在发现当时疑惑这个问题的时候,还蛮白痴的,还是那句话,异常和异步没半毛钱关系,相同的问题,同步也是这样进行处理的。

博文内容有点多,如果你不愿花时间看,可以直接记住这段话:如果你的应用程序请求访问很少(并发很小),异步和同步将是一样的效果,异步化改造是毫无意义的,而如果你的应用程序请求访问很多(并发很大),那么效果显而易见,如果使用异步将会为你省掉几台服务器的钱,但代码异步化并不能使你的应用程序执行速度加快(指的是代码执行速度),垃圾代码还是垃圾代码,并不会有任何的改善,所以,写好“好的代码”很重要!!!

重新理解:ASP.NET 异步编程(转)的更多相关文章

  1. Asp&period;Net异步编程-使用了异步&comma;性能就提升了吗&quest;

    Asp.Net异步编程 写在前面的话,很久没有写Blog了,不对,其实一致就没有怎么写过.今天有空,我也来写一篇Blog 随着.Net4.5的推出,一种新的编程方式简化了异步编程,在网上时不时的也看到 ...

  2. Asp&period;Net异步编程

    Asp.Net异步编程-使用了异步,性能就提升了吗? Asp.Net异步编程 写在前面的话,很久没有写Blog了,不对,其实一致就没有怎么写过.今天有空,我也来写一篇Blog 随着.Net4.5的推出 ...

  3. ASP&period;NET 异步编程

    ASP.NET 异步编程 相关博文: 异步编程 In .NET(回味无穷!!!) ASP.NET sync over async(异步中同步,什么鬼?) 本来这篇博文想探讨下异步中的异常操作,但自己在 ...

  4. 重新理解:ASP&period;NET 异步编程

    相关博文: 异步编程 In .NET(回味无穷!!!) ASP.NET sync over async(异步中同步,什么鬼?) 本来这篇博文想探讨下异步中的异常操作,但自己在做异步测试的时候,又对 A ...

  5. C&num;中的异步编程--探索await与async关键字的奥妙之处,原来理解和使用异步编程可以这么简单

    前言 await与async是C#5.0推出的新语法,关于await与async有很多文章讲解.但看完后有没有这样一种感觉,感觉这东西像是不错,但好像就是看不太懂,也不清楚该怎么使用.虽然偶有接触,但 ...

  6. 深入理解node&period;js异步编程:基础篇

    ###[本文是基础内容,大神请绕道,才疏学浅,难免纰漏,请各位轻喷] ##1. 概述 目前开源社区最火热的技术当属Node.js莫属了,作为使用Javascript为主要开发语言的服务器端编程技术和平 ...

  7. 深入理解 Python 异步编程&lpar;上&rpar;

    http://python.jobbole.com/88291/ 前言 很多朋友对异步编程都处于"听说很强大"的认知状态.鲜有在生产项目中使用它.而使用它的同学,则大多数都停留在知 ...

  8. Java网络编程中异步编程的理解

    目录 前言 一.异步,同步,阻塞和非阻塞的理解 二.异步编程从用户层面和框架层面不同角度的理解 用户角度的理解 框架角度的理解 三.为什么使用异步 四.理解这些能在实际中的应用 六.困惑 参考文章 前 ...

  9. 让我们再为C&num;异步编程Async正名

    本文版权归博客园和作者吴双本人共同所有.转载和爬虫必须在显要位置注明出处:http://www.cnblogs.com/tdws 半年前翻译了一系列很糟糕的异步编程文章,用异步的常用语来说:&quot ...

随机推荐

  1. CentOS7 SWAP 设置 (实测 笔记)

    首先查看当前的内存及swap情况(参数 -h,-m ) [root@centos ~]# free -h 查看swap信息,包括文件和分区的详细信息 [root@centos ~]# swapon - ...

  2. WM8978和VS1053B的区别

    WM8978是DAC和ADC芯片,不具备编解码的功能.不能把MP3格式或者WAV格式的文件解码出来.有播放和录音功能. 录音如果需要保存为MP3或者WAV格式,那么需要进行软件编码. VS1053B是 ...

  3. response的Writer输出数据的问题

    package cn.itcast.response; import java.io.IOException; import java.io.OutputStream; import java.io. ...

  4. POJ2002 二分查找&amp&semi;哈希

    问题重述: 给定整数n,以及n个点的坐标xi, yi.求这n个点可以组成的正方形的数目(每个点可重复使用). 分析: 根据正方形的性质,给定两个点就能确定可能构成的两个正方形的另外两个顶点.因此,只需 ...

  5. JBPM——MyEclipse开发环境的搭建

    第一次接触JBPM我不知道如何在工程中的应用.查了一些资料.大约在JBPM随着时代的发展有一定的了解.首先JBPM它是JBoss件平台的一个组成部分.是一个灵活的,易扩展的工作流管理系统,仅仅只是这个 ...

  6. 伸展树 Splay 模板

    学习Splay的时候参考了很多不同的资料,然而参考资料太杂的后果就是模板调出来一直都有问题,尤其是最后发现网上找的各种资料均有不同程度的错误. 好在啃了几天之后终于算是啃下来了. Splay也算是平衡 ...

  7. SpringBoot开发案例之打造私有云网盘

    前言 最近在做工作流的事情,正好有个需求,要添加一个附件上传的功能,曾找过不少上传插件,都不是特别满意.无意中发现一个很好用的开源web文件管理器插件 elfinder,功能比较完善,社区也很活跃,还 ...

  8. Python 爬虫入门&lpar;二&rpar;——爬取妹子图

    Python 爬虫入门 听说你写代码没动力?本文就给你动力,爬取妹子图.如果这也没动力那就没救了. GitHub 地址: https://github.com/injetlee/Python/blob ...

  9. js 生成随机炫彩背景

    在浏览 https://ghost.org/xxxx/ 时. 可以使用 background-size: cover; 加上很小的像素图,放大后实现炫彩背景效果. 使用 js canvas 随机生成小 ...

  10. 高通平台读写nv总结

    一,引言      1. 什么是NV       高通平台的NV,保存了系统运行过程中各个模块可能用到的一些参数值,它是以单个文件的形式保存在EFS中,但用户是不能随意访问的,只能通过QXDM来进行读 ...