ASP.NET的各种异步操作实现详细解析
【IT168技术】本系列文章为.net相关技术文章,全面分析了.net中相关技术要点,以下是该系列文章目录:
【我的Ajax服务端框架】 以及【我的MVC框架】
【细说Cookie】 和【细说 Form (表单)】
【Session,有没有必要使用它?】
【我心目中的Asp.net核心对象】
【用Asp.net写自己的服务框架】
【C#客户端的异步操作】
在上篇博客【C#客户端的异步操作】,我介绍了一些.net中实现异步操作的方法,在那篇博客中,我是站在整个.net平台的角度来讲述各种异步操作的实现方式,并针对各种异步操作以及不同的编程模型给出了一些参考建议。上篇博客谈到的内容可以算是异步操作的基础,今天我再来谈异步,专门来谈在ASP.NET平台下的各种异步操作。在这篇博客中,我主要演示在ASP.NET中如何使用各种异步操作。
在后续博客中,我还会分析ASP.NET的源码,解释为什么可以这样做,或者这样的原因是什么,以解密内幕的方式向您解释这些操作的实现原理。
由于本文是【C#客户端的异步操作】的续集,因此一些关于异步的基础内容,就不再过多解释了。如不理解本文的示例代码,请先看完那篇博文吧。
在【C#客户端的异步操作】的结尾,有一个小节【在Asp.net中使用异步】,我把我上次写好的示例做了个简单的介绍,今天我来专门解释那些示例代码。不过,在写博客的过程中,又做了一点补充,所以,请以前下载过示例代码的朋友,你们需要重新下载那些示例代码(还是那篇博客中)。
说明:那些代码都是在示范使用异步的方式调用【用Asp.net写自己的服务框架】博客中所谈到的那个服务框架,且服务方法的代码为:
在ASP.NET中使用异步
我在【C#客户端的异步操作】中提到一个观点: 对于服务程序而言,异步处理可以提高吞吐量。什么是服务程序,简单说来就是:可以响应来自网络请求的服务端程序。我们熟悉的ASP.NET显然是符合这个定义的。因此在ASP.NET程序中,适当地使用异步是可以提高服务端吞吐量的。这里所说的适当地使用异步,一般是说:当服务器的压力不大且很多处理请求的执行过程被阻塞在各种I/O等待(以网络调用为主)操作上时,而采用异步来减少阻塞工作线程的一种替代同步调用的方法。反之,如果服务器的压力已经足够大,或者没有发生各种I/O等待,那么,在此情况下使用异步是没有意义的。
在.net中,几乎所有的服务编程模型都是采用线程池处理请求任务的多线程工作模式。自然地,ASP.NET也不例外,根据【C#客户端的异步操作】的分析,我们就不能再使用一些将阻塞操作交给线程池的方法了。比如:委托的异步调用,直接使用线程池,都是不可取的。直接创建线程也是不合适的,因此那种方式会随着处理请求的数量增大而创建一大堆线程,最后也将会影响性能。因此,最终能被选用的只用BeginXxxxx/EndXxxxx方式。不过,我要补充的是:还有基于事件通知的异步模式也是一个不错的选择(我会用代码来证明),只要它是对原始BeginXxxxx/EndXxxxx方式的包装。
在【用Asp.net写自己的服务框架】中,我说过,ASP.NET处理请求是采用了一种被称为【管线】的方式,管线由HttpApplication控制并引发的一系列事件,由HttpHandler来处理请求,而HttpModule则更多地是一种辅助角色。还记得我在【C#客户端的异步操作】 总结的异步特色吗:【一路异步到底】。ASP.NET的处理过程要经过它们的处理,自然它们对于请求的处理也必须要支持异步。幸运地是,这些负责请求处理的对象都是支持异步的。今天的博客也将着重介绍它们的异步工作方式。
WebForm框架,做为ASP.NET平台上最主要且默认的开发框架,我自然也会全面地介绍它所支持的各种异步方式。
MVC框架从2.0开始,也开始支持异步,本文也会介绍如何在这个版本中使用异步。
该选哪个先出场呢?我想了很久,最后还是决定先请出处理请求的核心对象:HttpHandler 。
异步 HttpHandler
关于HttpHandler的接口,我在【用Asp.net写自己的服务框架】中已有介绍,这里就不再贴出它的接口代码了,只想说一句:那是个同步调用接口,它并没有异步功能。要想支持异步,则必须使用另一个接口:IHttpAsyncHandler
这个接口也很简单,只有二个方法,并且与【C#客户端的异步操作】 提到的BeginXxxxx/EndXxxxx设计方式差不多。如果这样想,那么后面的事件就好理解了。
在.net中,异步都是建立在IAsyncResult接口之上的,而BeginXxxxx/EndXxxxx是对这个接口最直接的使用方式。
下面我们来看一下如何创建一个支持异步的ashx文件(注意:代码中的注释很重要)。
实现其实是比较简单的,大致可以总结如下:
1. 在BeginProcessRequest()方法,调用要你要调用的异步开始方法,通常会是另一个BeginXxxxx方法。
2. 在EndProcessRequest()方法,调用要你要调用的异步结束方法,通常会是另一个EndXxxxx方法。
真的就是这么简单。
这里要说明一下,在【C#客户端的异步操作】中,我演示了如何使用.net framework中的API去实现完整的异步发送HTTP请求的调用过程,但那个过程需要二次异步,而这个IHttpAsyncHandler接口却只支持一次回调。因此,对于这种情况,就需要我们自己封装,将多次异步转变成一次异步。以下是我包装的一次异步的简化版本:
下面这个包装类非常有用,我后面的示例还将会使用它。它也示范了如何创建自己的IAsyncResult封装。因此建议仔细阅读它。(注意:代码中的注释很重要)
对于这个包装类来说,最关键还是MyHttpAsyncResult的实现,它是异步模式的核心。