本来这篇博文是想讨论一下异步中的异常运行,但是在做异步测试的时候,对ASP.NET异步有了新的认识。对异步的理解还存在一些问题。先把这篇博文的三个困惑列一下:asyncawait是什么鬼???异步操作出现异常怎么办?如果异步操作发生异常(有或没有catchthrow),Application_Error会捕获吗?之前在异步中测试过同步(很多情况下),这次我们写比较复杂的测试代码(异步中的异步),代码如下:[Route("")][HttpGet]publicasyncTaskIndex(){System.Diagnostics.Debug.WriteLine("Thread.CurrentThread.ManagedThreadId1:"+Thread.CurrentThread.ManagedThreadId);varresult=awaitTest();System.Diagnostics.Debug.WriteLine("Thread.CurrentThread.ManagedThreadId6:"+Thread.CurrentThread.ManagedThreadId);returnresult;}publicstaticasyncTaskTest(){System.Diagnostics.Debug.WriteLine("Thread.CurrentThread.ManagedThreadId2:"+Thread.CurrentThread.ManagedThreadId);使用(varclient=newHttpClient()){varresponse=awaitclient.GetAsync("http://stackoverflow.com/questions/14996529/why-is-my-async-asp-net-web-api-controller-阻塞主线程");awaitTest2();System.Diagnostics.Debug.WriteLine("Thread.CurrentThread.ManagedThreadId5:"+Thread.CurrentThread.ManagedThreadId);returnawaitresponse.Content.ReadAsStringAsync();}}publicstaticasyncTask<字符串>Test2(){System.Diagnostics.Debug.WriteLine("Thread.CurrentThread.ManagedThreadId3:"+Thread.CurrentThread.ManagedThreadId);使用(varclient=newHttpClient()){varresponse=awaitclient.GetAsync("http://stackoverflow.com/questions/33408905/pgadminiii-bug-on-query-tool");System.Diagnostics.Debug.WriteLine("Thread.CurrentThread.ManagedThreadId4:"+Thread.CurrentThread.ManagedThreadId);returnawaitresponse.Content.ReadAsStringAsync();}}输出结果(执行第四次):Thread.CurrentThread.ManagedThreadId1:8Thread.CurrentThread.ManagedThreadId2:8Thread.CurrentThread.ManagedThreadId3:6Thread.CurrentThread.ManagedThreadId4:6Thread.CurrentThread.ManagedThreadId5:6Thread.CurrentThread.ManagedThreadId5:6Thread.ManagedThreadId5:6Thread.ManagedThreadIthread.ManagedThreadIead.CurrentThread.ManagedThreadId1:7Thread.CurrentThread.ManagedThreadId2:7Thread.CurrentThread.ManagedThreadId3:8Thread.CurrentThread.ManagedThreadId4:7Thread.CurrentThread.ManagedThreadId5:7Thread.CurrentThread.ManagedThreadId6:7Thread.CurrentThread.ManagedThread2CurrentThreadId1:5CurrentThread.ManagedThreadId3:5Thread.CurrentThread.ManagedThreadId4:6Thread.CurrentThread.ManagedThreadId5:6Thread.CurrentThread.ManagedThreadId6:6Thread.CurrentThread.ManagedThreadId1:8Thread.CurrentThread.ManagedThreadId2:8Thread.CurrentThread.ManagedThreadId3:8Thread.CurrentThread.ManagedThreadId4:8Thread.CurrentThreadManagedThreadId5:8Thread.CurrentThread.ManagedThreadId6:8这个测试方法,我执行了无数次,大致就是上面四种情况,当时看到输出结果,其实很乱,我和大家一样,我有一些想法问题:你真的是异步编程吗?为什么线程如此奇怪?而最后一个只有一个线程,这和同步有什么区别???针对上面的问题,我想了很久,心里也有些怀疑:你天天写asyncawait的代码,你真的懂吗???然后又找到上面jesseliu的博文,反复看了很多文章,终于有了一些“顿悟”,结合上面的测试代码,大致画了一个示意图:结合上面的图,我说了一个根据我自己的理解,在测试的时候,HttpClient.GetAsync试图让它执行的时间更长。比如请求的URL可以是stackoverflow或者github(你懂的!),因为有时间差,所以我们更能理解线程的执行,“线程1,线程1x,线程3x,线程4x”中上图等等,这些不是不同的线程,也就是说,线程1可能等于线程1x,也可能等于线程3x。.从上面的输出结果可以看出,线程x用来表示两次输出之间经历的等待次数,这就证明了一个疑问:await不一定会创建和之前不同的线程。究竟什么是异步???我个人认为async是个伪概念,await才是本质。一个线程可以响应多个请求。如果是同步编程,一个线程在处理某个请求时被阻塞了(比如HttpClient.GetAsync网络操作),那么这个线程就会等待它处理。在这个等待过程中,其他请求不能再使用这个线程,而且由于IIS线程池的线程数有限,在同步编程下,高并发会很头疼。试想一下,如果线程池中的线程数是100个,当这100个线程同时处理100个请求时,会很伤心的被阻塞。此时第101个请求不会被执行,则并发为100。从上面接下去,同样的流程,如果是异步编程,会发生什么?比如一个线程在处理某个请求的时候执行了await操作,这个线程就会被释放回线程池,然后等待。在等待过程中,原线程可以处理其他请求或本次请求的其他请求。操作,注意等待不是线程等待,而是操作等待。我不是很了解这个地方。如果是线程等待,则表示这个线程会等待它完成,这和同步编程是一样的,所以这种理解是错误的,可以这样理解:await等待期间没有线程!!!然后连接到上面,等待操作完成后,此时会从线程池中随机取出一个线程继续执行。获取到的线程可能只是await操作释放的,也可能是其他线程,如上图2-6操作是这样的,一张图胜过千言万语:了解整个过程后,你会明白什么是asyncawait吗?它真正发挥作用在哪里?简单总结几点:async异步网络处理最明显(HttpClient请求或数据库连接):这个大家都很清楚,也很容易理解。如果是其他操作,比如一个异步的方法,你做了很多耗时的计算,那么这个异步就没有效果了。说白了,就是和synchronous一样。对于网络操作,我们一般不处理。我们只是在发起请求后等待它完成,所以此时这里执行的线程可以释放,会去线程池,网络操作完成后,从线程池中随机取出一个线程继续执行。async异步并不是真正的“异步”:它是什么意思?如果仔细看上面测试的输出,会发现ManagedThreadId1-6是顺序输出的,而不是先输出ManagedThreadId4再输出ManagedThreadID3,所以异步和同步的执行过程是一样的,执行时间是同一个请求下是的,从某种意义上说,上面的异步测试什么都测试不了(从测试结果可以看出)。异步不能减少你的执行时间,而是增加你的请求执行次数。这个东西说白了就是,其实就是并发量。async异步的本质就是await:这个前面已经讲过了。async异步的本质准确来说就是await期间的线程回收,完成后的线程切换。这个操作最大的价值在于避免了线程的浪费等待,充分利用了线程,这有点类似于地主不能容忍奴隶做无意义的事情,而是希望他们24小时工作。此外,在ASP.NET应用程序中,我们可以使用Thread.CurrentThread来访问当前执行线程。之前想做这样一个测试,让当前执行线程Sleep一段时间,看看其他线程会不会执行,但是Thread.CurrentThread没有Sleep方法,必须通过这种方式访问??Thread.Sleep(intmillisecondsTimeout).如果这样执行这段代码,当前线程会Sleep,但是其他线程不会在它Sleeping的时候继续执行。为什么??因为CPU同一时间只能执行一个线程。了解了asyncawait是什么之后,博文开头关于异步操作中异常的剩下两个问题就很好理解了:如何处理异步操作中的异常?:和同步一样处理。同步和异步都报错。有些人可能会有这样的疑问。比如测试代码中的IndexAction执行到awaitTest内部操作时,突然抛出异常,然后就想当然了,既然是异步执行的Test方法,应该不会影响Index吧?其实你执行之后,你会发现Index页面还是会抛异常,所以这个异常和异步无关。如果异步操作发生异常(有或没有catchthrow),Application_Error会捕获吗?:没有catch,Application_Error会被捕获;如果有catch但没有throw,则不会捕获到Application_Error;如果有catch和throw,Application_Error将被捕获。如果我们想让一个异步方法抛出异常而不影响其他异步方法,那么我们应该catch而不是throw,比如我们的测试代码:[Route("")][HttpGet]publicasyncTaskIndex(){System.Diagnostics.Debug.WriteLine("Thread.CurrentThread.ManagedThreadId1:"+Thread.CurrentThread.ManagedThreadId);varresult=awaitTest();System.Diagnostics.Debug.WriteLine("Thread.CurrentThread.ManagedThreadId6:"+Thread.CurrentThread.ManagedThreadId);returnresult;}publicstaticasyncTaskTest(){try{System.Diagnostics.Debug.WriteLine("Thread.CurrentThread.ManagedThreadId2:"+Thread.CurrentThread.ManagedThreadId);使用(varclient=newHttpClient()){varresponse=awaitclient.GetAsync("http://stackoverflow.com/questions/33408905/pgadminiii-bug-on-query-tool");System.Diagnostics.Debug.WriteLine("Thread.CurrentThread.ManagedThreadId3:"+Thread.CurrentThread.ManagedThreadId);thrownewException("testexception");//这里有异常returnawaitresponse.Content.ReadAsStringAsync();}}catch(Exceptionex){System.Diagnostics.Debug.WriteLine("异常信息:"+ex.Message);return"";//throwex;}}这样的作用是Index页面不会报错,也不会影响其他方法的执行。现在我发现,当我对这个问题感到疑惑时,我真是个白痴。还是那句话。异常和异步是没有关系的。同样的问题,同步也是这样处理的。博文内容有点多,如果不想花时间看的话,可以直接记住这段话:如果你的应用请求访问量很少(并发量很小),异步和同步会有同样的效果,异步转换是没有意义的,如果你的应用请求访问很多(很多并发),那么效果很明显。如果用异步的话,会省下几台服务器的钱,但是代码的异步化不会让你的应用执行的更快(指代码执行速度),垃圾代码还是垃圾代码,不会有任何改善,所以很写“好代码”很重要!!!