王朝网络
分享
 
 
 

ASP.NET 2.0 中的异步页功能应用

王朝asp·作者佚名  2006-11-24
宽屏版  字体: |||超大  

下载本文源代码:WickedCode0510.exe

ASP.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)

);

接下来的操作比较有趣。该页经历其常规处理生命周期,直到 PreRender 事件刚刚引发之后。然后,ASP.NET 调用使用 AddOnPreRenderCompleteAsync 注册的 Begin 方法。Begin 方法的任务是启动诸如数据库查询或 Web 服务调用的异步操作,并立即返回。此时,分配给该请求的线程返回到线程池。此外,Begin 方法返回 IAsyncResult,它允许 ASP.NET 确定异步操作完成的时间,这个时候 ASP.NET 从线程池提取线程并调用 End 方法。当 End 返回之后,ASP.NET 执行该页生命周期其余的部分,包括呈现阶段。在 Begin 返回以及调用 End 之间,该请求处理线程可以自由地服务于其他请求,直至调用 End 且延迟呈现为止。由于 2.0 版的 .NET Framework 提供多种执行异步操作的方式,因此,您甚至无需实现 IasyncResult。反之,Framework 替您实现。

图 1 中的代码隐藏类提供一个示例。响应页包含一个 ID 为“Output”的 Label 控件。该页使用 System.Net.HttpWebRequest 类提取 http://MSDN.microsoft.com 的内容。然后,它分析返回的 HTML,并将它发现的全部 HREF 目标列表写出到 Label 控件。

图1

using System;

using System.Web;

using System.Web.UI;

using System.Web.UI.WebControls;

using System.Net;

using System.IO;

using System.Text;

using System.Text.RegularExpressions;

public partial class AsyncPage : System.Web.UI.Page

{

private WebRequest _request;

void Page_Load (object sender, EventArgs e)

{

AddOnPreRenderCompleteAsync (

new BeginEventHandler(BeginAsyncOperation),

new EndEventHandler (EndAsyncOperation)

);

}

IAsyncResult BeginAsyncOperation (object sender, EventArgs e,

AsyncCallback cb, object state)

{

_request = WebRequest.Create("http://msdn.microsoft.com");

return _request.BeginGetResponse (cb, state);

}

void EndAsyncOperation (IAsyncResult ar)

{

string text;

using (WebResponse response = _request.EndGetResponse(ar))

{

using (StreamReader reader = new StreamReader(response.GetResponseStream()))

{

text = reader.ReadToEnd();

}

}

Regex regex = new Regex ("href\\s*=\\s*\"([^\"]*)\"", RegexOptions.IgnoreCase);

MatchCollection matches = regex.Matches(text);

StringBuilder builder = new StringBuilder(1024);

foreach (Match match in matches)

{

builder.Append (match.Groups[1]);

builder.Append("<br/>");

}

Output.Text = builder.ToString ();

}

}

由于 HTTP 请求需要较长时间才能返回,因此,AsyncPage.aspx.cs 异步执行对它的处理。它在 Page_Load 中注册 Begin 和 End 方法,并且在 Begin 方法中,它调用 HttpWebRequest.BeginGetResponse 启用一个异步 HTTP 请求。BeginAsyncOperation 将由 BeginGetResponse 返回的 IAsyncResult 返回到 ASP.NET,导致当 HTTP 请求完成时,ASP.NET 调用 EndAsyncOperation。EndAsyncOperation 进而分析该内容并将结果写入 Label 控件,之后进行呈现,并且 HTTP 响应返回到浏览器。

图 2 同步和异步页处理

图 2 说明 ASP.NET 2.0 同步和异步页之间的区别。当请求同步页时,ASP.NET 为该请求分配线程池中的一个线程,并在该线程上执行页。如果该请求停止执行 I/O 操作,则挂起线程,直到完成操作,从而可以完成该页的生命周期。相反,异步页通常通过 PreRender 事件执行。然后,调用使用 AddOnPreRenderCompleteAsync 注册的 Begin 方法,之后,该请求处理线程返回线程池。Begin 启动一个异步 I/O 操作,当该操作完成时,ASP.NET 从线程池提取另一个线程并调用 End 方法,并且在该线程上执行该页生命周期的其余部分。

图 3 跟踪输出显示异步页的异步点

对 Begin 的调用标记该页的“异步点”。图 3 中的跟踪准确显示异步点发生在何处。如果调用,则必须在异步点之前调用 AddOnPreRenderCompleteAsync — 即,不晚于该页的 PreRender 事件。

异步数据绑定

通常情况下,ASP.NET 页并不使用 HttpWebRequest 直接请求其他页,但它们通常查询数据库并对结果进行数据绑定。因此,您将如何使用异步页执行异步数据绑定呢?图 4 中的代码隐藏类显示进行此操作的一种方式。

using System;

using System.Data;

using System.Data.SqlClient;

using System.Web;

using System.Web.UI;

using System.Web.UI.WebControls;

using System.Web.Configuration;

public partial class AsyncDataBind : System.Web.UI.Page

{

private SqlConnection _connection;

private SqlCommand _command;

private SqlDataReader _reader;

protected void Page_Load(object sender, EventArgs e)

{

if (!IsPostBack)

{

// Hook PreRenderComplete event for data binding

this.PreRenderComplete += new EventHandler(Page_PreRenderComplete);

// Register async methods

AddOnPreRenderCompleteAsync(

new BeginEventHandler(BeginAsyncOperation),

new EndEventHandler(EndAsyncOperation)

);

}

}

IAsyncResult BeginAsyncOperation (object sender, EventArgs e, AsyncCallback cb, object state)

{

string connect = WebConfigurationManager.ConnectionStrings

["PubsConnectionString"].ConnectionString;

_connection = new SqlConnection(connect);

_connection.Open();

_command = new SqlCommand("SELECT title_id, title, price FROM titles", _connection);

return _command.BeginExecuteReader (cb, state);

}

void EndAsyncOperation(IAsyncResult ar)

{

_reader = _command.EndExecuteReader(ar);

}

protected void Page_PreRenderComplete(object sender, EventArgs e)

{

Output.DataSource = _reader;

Output.DataBind();

}

public override void Dispose()

{

if (_connection != null) _connection.Close();

base.Dispose();

}

}

AsyncDataBind.aspx.cs 与 AsyncPage.aspx.cs 使用相同的 AddOnPreRenderCompleteAsync 模式。但是,AsyncDataBind.aspx.cs 的 BeginAsyncOperation 方法调用 ADO.NET 2.0 中的新方法 SqlCommand.BeginExecuteReader(而非 HttpWebRequest.BeginGetResponse),以执行一个异步数据库查询。当调用完成时,EndAsyncOperation 调用 SqlCommand.EndExecuteReader 以获取 SqlDataReader,然后将其存储在私有字段中。在用于 PreRenderComplete 事件(在异步操作完成但呈现该页之前引发)的事件处理程序中,AsyncDataBind.aspx.cs 之后将 SqlDataReader 绑定到 Output GridView 控件。从外观上看,该页类似于使用 GridView 呈现数据库查询结果的普通(同步)页。但是在内部,该页更具可伸缩性,因为它并不挂起线程池线程以等待查询返回。

异步调用 Web 服务

另一个通常由 ASP.NET Web 页执行的、与 I/O 相关的任务是调出 Web 服务。由于 Web 服务调用花费较长时间才能返回,因此,执行它们的页是用于异步处理的理想选择。

图5

using System;

using System.

[1] [2] [3] 下一页

 
 
 
免责声明:本文为网络用户发布,其观点仅代表作者个人观点,与本站无关,本站仅提供信息存储服务。文中陈述内容未经本站证实,其真实性、完整性、及时性本站不作任何保证或承诺,请读者仅作参考,并请自行核实相关内容。
2023年上半年GDP全球前十五强
 百态   2023-10-24
美众议院议长启动对拜登的弹劾调查
 百态   2023-09-13
上海、济南、武汉等多地出现不明坠落物
 探索   2023-09-06
印度或要将国名改为“巴拉特”
 百态   2023-09-06
男子为女友送行,买票不登机被捕
 百态   2023-08-20
手机地震预警功能怎么开?
 干货   2023-08-06
女子4年卖2套房花700多万做美容:不但没变美脸,面部还出现变形
 百态   2023-08-04
住户一楼被水淹 还冲来8头猪
 百态   2023-07-31
女子体内爬出大量瓜子状活虫
 百态   2023-07-25
地球连续35年收到神秘规律性信号,网友:不要回答!
 探索   2023-07-21
全球镓价格本周大涨27%
 探索   2023-07-09
钱都流向了那些不缺钱的人,苦都留给了能吃苦的人
 探索   2023-07-02
倩女手游刀客魅者强控制(强混乱强眩晕强睡眠)和对应控制抗性的关系
 百态   2020-08-20
美国5月9日最新疫情:美国确诊人数突破131万
 百态   2020-05-09
荷兰政府宣布将集体辞职
 干货   2020-04-30
倩女幽魂手游师徒任务情义春秋猜成语答案逍遥观:鹏程万里
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案神机营:射石饮羽
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案昆仑山:拔刀相助
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案天工阁:鬼斧神工
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案丝路古道:单枪匹马
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案镇郊荒野:与虎谋皮
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案镇郊荒野:李代桃僵
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案镇郊荒野:指鹿为马
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案金陵:小鸟依人
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案金陵:千金买邻
 干货   2019-11-12
 
>>返回首页<<
推荐阅读
 
 
频道精选
 
静静地坐在废墟上,四周的荒凉一望无际,忽然觉得,凄凉也很美
© 2005- 王朝网络 版权所有