ASP.NET页面剖析异步页面

    ASP.NET页面会被HTTP处理程序作为Page类的实例处理。每个请求会占用ASP.NET线程池中的一个线程,在请求完毕后该线程才会被释放。如果被请求的页面频繁地启动外部的、高耗时的任务时,经常会出现ASP.NET进程闲置,但池中没有空闲的线程来处理新入的其他页面的请求。这种情况下,创建异面页面可以减轻这个问题。

    异步ASP.NET页面的构建涉及两个方面: @Page指令的一个新属性Async,以及注册若干异步执行的任务。异步任务可以通过两种途径注册:一种是为PreRenderComplete事件定义异步处理程序Begin/End对(AddOnPreRenderCompleteAsync方法用于将异步事件处理程序添加到页面的PreRenderComplete事件中);另一种是创建代表异步任务的PageAsyncTask对象,PageAsyncTask类代表要以异步方式执行任务(RegisterAsyncTask方法能够接受一个PageAsyncTask对象,返回void)。

AddOnPreRenderCompleteAsyncRegisterAsyncTask的区别:

    从功能上讲,这两个方法几乎相同,二者都会将请求的执行分为两部分——分别在同步点前后。他们之间的区别在于:第一个区别在逻辑上,RegisterAsyncTask是一个用于在一个页面中运行多个异步任务的API(而不能跨越多个带有Async=true属性的异步页面),而AddOnPreRenderCompleteAsync是专门为多个异步页面设计的。RegisterAsyncTask执行End处理程序时,所处线程的上下文比AddOnPreRenderCompleteAsync所处线程的更丰富。该线程上下文包括模拟(impersonation)和HTTP上下文信息,该信息在一般异步页面的End处理程序所处线程中不存在。此外,RegisterAsyncTask还允许设置超时值,确保任何任务消耗的时间不会超过指定的秒数。另一个区别在:RegisterAsyncTask有利于实现对远程源的多个调用。我们只需设置一个布尔类型的标志,便会获得并行执行的能力,而不必要自行创建并管理自定义的IAsyncResult对象。

前台代码示例:

View Code
 1 <%@ Page Language="C#" AutoEventWireup="true" CodeBehind="StreamDownload.aspx.cs" Inherits="WebTestDemo.AsyncPage.StreamDownload" Async="true" Trace="true"%>
 2 
 3 <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
 4 
 5 <html xmlns="http://www.w3.org/1999/xhtml">
 6 <head runat="server">
 7     <title></title>
 8 </head>
 9 <body>
10     <form id="form1" runat="server">
11     <div>
12         <%=msdnData%>
13     </div>
14     </form>
15 </body>
16 </html>

注意:以上@Page指令的Async和Trace属性。Async属性使页面能够为PreRenderComplete事件注册异步处理程序,Trace属性使页面能够显示页面跟踪信息。

后台代码示例:

View Code
 1 /// <summary>
 2     /// 异步下载MSDN首页实例
 3     /// </summary>
 4     public partial class StreamDownload : System.Web.UI.Page
 5     {
 6         const string connectionToMSDN = "http://msdn.microsoft.com";
 7         private WebRequest req;
 8         public string msdnData;
 9 
10         protected override void OnInit(EventArgs e)
11         {
12             //将跟踪消息写入页面跟踪日志
13             Trace.Warn("当前线程:" + Thread.CurrentThread.ManagedThreadId.ToString());
14         }
15 
16         protected void Page_Load(object sender, EventArgs e)
17         {
18             //AddOnPreRenderCompleteAsync方法
19             AddOnPreRenderCompleteAsync(
20                 new BeginEventHandler(BeginTask),
21                 new EndEventHandler(EndTask));
22 
23             //RegisterAsyncTask方法
24             //PageAsyncTask task = new PageAsyncTask(
25             //    new BeginEventHandler(BeginTask),
26             //    new EndEventHandler(EndTask),
27             //    null,
28             //    null);
29             //RegisterAsyncTask(task);
30         }
31 
32         IAsyncResult BeginTask(object sender, EventArgs e, AsyncCallback cb, object state)
33         {
34             Trace.Warn("开始异步 当前线程:" + Thread.CurrentThread.ManagedThreadId.ToString());
35             //准备一个Web请求
36             req = WebRequest.Create(connectionToMSDN);
37             //开始操作并返回IAsyncResult对象
38             return req.BeginGetResponse(cb, state);
39             
40         }
41 
42         void EndTask(IAsyncResult ar)
43         {
44             //这段代码将调用一个池里的线程
45             string text;
46             using (WebResponse response = req.EndGetResponse(ar))
47             {
48                 StreamReader reader;
49                 using (reader = new StreamReader(response.GetResponseStream()))
50                 {
51                     text = reader.ReadToEnd();
52                 }
53                 
54                 msdnData = ProcessFeed(text);
55             }
56             Trace.Warn("结束异步 当前线程:" + Thread.CurrentThread.ManagedThreadId.ToString());
57         }
58 
59         protected override void OnSaveStateComplete(EventArgs e)
60         {
61             Trace.Warn("当前线程:" + Thread.CurrentThread.ManagedThreadId.ToString());
62         }
63 
64         string ProcessFeed(string feed)
65         {
66             //来自XML输入,创建页面输出
67             return Server.HtmlDecode(feed);
68         }
69     }

图1:从MSDN页面下载的内容

 图2:跟踪的请求详细信息

    图2能看出,当页面加载到达PreRenderComplete阶段前,异步页面会一直执行,随后被阻塞,等待异步操作完成。当该操作最终完成后,页面会从PreRenderComplete阶段继续执行。根据ASP.NET设计,在其中有一个针对异步操作的“展开点”(unwind point)(也称为“同步点”[async point]),该点位于PreRender和PreRenderComplete事件之间。当页面接收到PreRender事件时,该同步点尚未到达。而页面接收到PreRenderComplete时,刚好超过该同步点。

    当程序处理到PreRender之后时,线程会被释放,执行外部的任务, 异步任务执行完成后,HTTP运行库会再次处理请求,即从PreRenderComplete阶段开始继续完成其页面生命周期的其他阶段(如图2中的后续步骤)。

原文地址:https://www.cnblogs.com/PongorXi/p/2559369.html