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://stackoverflow.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://stackoverflow.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 的博文,反复读了很多篇,最后终于有了一些“顿悟”,结合上面的测试代码,我大致画了一张示意图:

结合上面的图,我说一下自己的理解,在做测试的时候,HttpClient.GetAsync 尽量让它执行时间长些,比如请求的 URL 可以是 stackoverflow 或 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 操作就是这样,一图胜千言:

了解了整个过程之后,你才会明白 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://stackoverflow.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.Net异步编程-使用了异步,性能就提升了吗?

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

  2. Asp.Net异步编程

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

  3. ASP.NET 异步编程

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

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

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

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

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

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

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

  7. 深入理解 Python 异步编程(上)

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

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

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

  9. 让我们再为C#异步编程Async正名

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

随机推荐

  1. PHPExcel中open_basedir restriction in effect的解决方法

    用PHPExcel做导出execl的时候发现在本地没有问题,但是把网站传到租用的服务器的时候就报错,具体如下: Warning: realpath() [function.realpath]: ope ...

  2. BIEE从底层表结构向上更新

    影响BIEE查询结果的几个因素: 1.数据库表结构变化后,RPD如何处理? 更新物理层 增加.删除.修改表名. 添加字段:右键点击连接池,点击导入元数据.勾掉关键字,重新导入那张表. 删除字段:直接右 ...

  3. Effective Java 34 Emulate extensible enums with interfaces

    Advantage Disadvantage Enum types Clarity Safety Ease of maintenance. None extensibility Typesafe en ...

  4. Effective Java 77 For instance control, prefer enum types to readResolve

    The readResolve feature allows you to substitute another instance for the one created by readObject ...

  5. 【故障处理】ORA-30012的解决过程

    [故障处理]ORA-30012的解决过程 1  BLOG文档结构图 2  前言部分 2.1  导读和注意事项 各位技术爱好者,看完本文后,你可以掌握如下的技能,也可以学到一些其它你所不知道的知识,~O ...

  6. 测试mysql的sql语句预编译效果

    玩Oracle的都比较关注shared pool,特别是library cache,在使用了绑定变量(预编译sql)之后确实能得到很大的性能提升.现在在转Mysql之后特别是innodb很多东西都还能 ...

  7. UC 浏览器远程调试手机web网页记录

    浏览器远程调试插件有很多,本来要使用chrome浏览器的调试插件的,但是需要FQ才能使用(公司网络有限制,果断放弃),最终选择使用UC浏览器的. 其实UC官网插件使用已经介绍的很详细了,但是有几处坑需 ...

  8. Mac SVN ignore 等相关

    OSX自带了SVN命令行,通过终端就可以使用了. 一.SVN ignore Mac的SVN想把node_modules 忽略,即svn status时(svn st缩写)不显示node_nodules ...

  9. 利用Keydown事件阻止用户输入

    先了解下各事件的区别 keydown:在控件有焦点的情况下按下键时发生 keypress:在控件有焦点的情况下按下键时发生 keyup:   在控件有焦点的情况下释放键时发生 意义 keypress主 ...

  10. python 练习多级菜单思路

    只写了一个zj的三级菜单,后面的功能没写 #-*- coding :utf-8 -*- print """ 你可以输入省份然后根据市县输入 ""&qu ...