Welcome 微信登录

首页 / 网页编程 / ASP.NET / ASP.NET 2.0中的异步页功能应用

ASP.NET 2.0中的异步页功能应用2010-12-08 MSDN Jeff ProsiseASP.NET 2.0 提供了大量新功能,其中包括声明性数据绑定和母版页,成员 和角色管理服务等。但我认为最棒的功能是异步页,接下来让我告诉您其中的原 因。

当 ASP.NET 接收针对页的请求时,它从线程池中提取一个线程并将请求分配 给该线程。一个普通的(或同步的)页在该请求期间保留线程,从而防止该线程 用于处理其他请求。如果一个同步请求成为 I/O 绑定(例如,如果它调用一个 远程 Web 服务或查询一个远程数据库,并等待调用返回),那么分配给该请求 的线程在调用返回之前处于挂起状态。这影响了可伸缩性,原因是线程池的可用 线程是有限的。如果所有请求处理线程全部阻塞以等待 I/O 操作完成,则其他 请求排入队列等待线程释放。最好的情况是吞吐量减少,因为请求等待较长的时 间才能得到处理。最坏的情况则是该队列填满,并且 ASP.NET 因 503“Server Unavailable”错误使后续请求失败。

异步页为由 I/O 绑定的请求引起的问题提供优秀的解决方案。页处理从线程 池线程开始,但是当一个异步 I/O 操作开始响应 ASP.NET 的信号之后,该线程 返回线程池。当该操作完成时,ASP.NET 从线程池提取另一个线程,并完成该请 求的处理。由于线程池线程得到了更高效的使用,因此提高了可伸缩性。那些挂 起等待 I/O 完成的线程现在可用于服务其他请求。直接的受益方是不执行长时 间 I/O 操作并因此可以快速进出管线的请求。长时间等待进入管线会对此类请 求的性能带来不小的负面影响。

ASP.NET 2.0 Beta 2 异步页基础结构的相关文档很少。让我们展望一下异步 页的前景,从而弥补这点不足。请记住,本专栏涉及 ASP.NET 2.0 和 .NET Framework 2.0 的测试版本。

ASP.NET 1.x 中的异步页

ASP.NET 1.x 本质上不支持异步页,但是通过坚韧的努力和不懈地创新可以 生成异步页。有关更多概述信息,请参阅相关资料

这里的技巧是,在一个页的代码隐藏类中实现 IhttpAsyncHandler,从而提 示 ASP.NET 通过调用 IHttpAsyncHandler.BeginProcessRequest 来处理请求, 而不是通过调用该页的 IHttpHandler.ProcessRequest 方法。然后,您的 BeginProcessRequest 实现可以启动另一个线程。该线程调用 base.ProcessRequest,使得页进入其常规请求处理生命周期(完成诸如 Load 和 Render 的事件),但是在非 ThreadPool 线程上例外。同时,启动新线程之 后 BeginProcessRequest 立即返回,从而允许执行 BeginProcessRequest 的线 程返回线程池。

这是基本思想,但细节中还有很多注意事项。其中,您需要实现 IAsyncResult,并从 BeginProcessRequest 中返回它。这通常意味着创建一个 ManualResetEvent 对象,并且当 ProcessRequest 在后台线程中返回时向其发 送信号。此外,您必须提供调用 base.ProcessRequest 的线程。遗憾的是,多 数用于将工作移到后台线程的常规技术(包括 Thread.Start、 ThreadPool.QueueUserWorkItem 和异步委托)在 ASP.NET 应用程序中都是起反 作用的,因为它们或者从线程池“偷盗”线程,或者有不受限制的线程增长的危 险。正确的异步页实现使用自定义线程池,但自定义线程池类不容易编写。

主要是在 ASP.NET 1.x 中生成异步页并非不可能,而是有些乏味。在尝试一 、两次之后,您不禁会想一定会有更好的方法。目前,这个好方法就是 ASP.NET 2.0。

ASP.NET 2.0 中的异步页

ASP.NET 2.0 极大地简化了生成异步页的方式。首先使用该页的 @ Page 指 令引入 Async=“true” 属性,如下所示:

在后台,这会通知 ASP.NET 在该页中实现 IhttpAsyncHandler。接下来,您 在该页生存期的早期(例如,在 Page_Load 时)调用新的 Page.AddOnPreRenderCompleteAsync 方法来注册一个 Begin 方法和一个 End 方法,如以下代码所示:

AddOnPreRenderCompleteAsync (
new BeginEventHandler(MyBeginMethod),
new EndEventHandler (MyEndMethod)
);