|
马上注册,结交更多好友,享用更多功能,让你轻松玩转社区。
您需要 登录 才可以下载或查看,没有帐号?立即注册
x
以前学了大概半年时间的asp(没有机会做大系统,最多是自己对公司系统做些调整和修改还有一些小程序)。应该说开始接触asp.net是今年元月5号的事。现在很想把公司的系统重新用.net来架构,却不知道如何下手。asp.net|异步 下载本文源代码:WickedCode0510.exe
ASP.NET2.0供应了大批新功效,个中包含声明性数据绑定和母版页,成员和脚色办理服务等。但我以为最棒的功效是异步页,接上去让我告知您个中的缘故原由。
当ASP.NET吸收针对页的哀求时,它从线程池中提取一个线程并将哀求分派给该线程。一个一般的(或同步的)页在该哀求时代保存线程,从而避免该线程用于处置其他哀求。假如一个同步哀求成为I/O绑定(比方,假如它挪用一个远程Web服务或查询一个远程数据库,并守候挪用前往),那末分派给该哀求的线程在挪用前往之前处于挂起形态。这影响了可伸缩性,缘故原由是线程池的可用线程是无限的。假如一切哀求处置线程全体堵塞以守候I/O操纵完成,则其他哀求排进行列守候线程开释。最好的情形是吞吐量削减,由于哀求守候较长的工夫才干失掉处置。最坏的情形则是该行列填满,而且ASP.NET因503“ServerUnavailable”毛病使后续哀求失利。
异步页为由I/O绑定的哀求引发的成绩供应优异的办理计划。页处置从线程池线程入手下手,可是当一个异步I/O操纵入手下手呼应ASP.NET的旌旗灯号以后,该线程前往线程池。当该操纵完成时,ASP.NET从线程池提取另外一个线程,并完成该哀求的处置。因为线程池线程失掉了更高效的利用,因而进步了可伸缩性。那些挂起守候I/O完成的线程如今可用于服务其他哀求。间接的受害方是不实行长工夫I/O操纵并因而能够疾速收支管线的哀求。长工夫守候进进管线会对此类哀求的功能带来不小的负面影响。
ASP.NET2.0Beta2异步页基本布局的相干文档很少。让我们瞻望一下异步页的远景,从而填补这点不敷。请记着,本专栏触及ASP.NET2.0和.NETFramework2.0的测试版本。
ASP.NET1.x中的异步页
ASP.NET1.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.NET1.x中天生异步页并不是不成能,而是有些有趣。在实验1、两次以后,您不由会想必定会有更好的办法。今朝,这个好办法就是ASP.NET2.0。
ASP.NET2.0中的异步页
ASP.NET2.0极年夜地简化了天生异步页的体例。起首利用该页的@Page指令引进Async=“true”属性,以下所示:
在背景,这会关照ASP.NET在该页中完成IhttpAsyncHandler。接上去,您在该页保存期的初期(比方,在Page_Load时)挪用新的Page.AddOnPreRenderCompleteAsync办法来注册一个Begin办法和一个End办法,如以下代码所示:
- AddOnPreRenderCompleteAsync(newBeginEventHandler(MyBeginMethod),newEndEventHandler(MyEndMethod));
复制代码 接上去的操纵对照风趣。该页履历其惯例处置性命周期,直到PreRender事务方才激发以后。然后,ASP.NET挪用利用AddOnPreRenderCompleteAsync注册的Begin办法。Begin办法的义务是启动诸如数据库查询或Web服务挪用的异步操纵,并当即前往。此时,分派给该哀求的线程前往到线程池。别的,Begin办法前往IAsyncResult,它同意ASP.NET断定异步操纵完成的工夫,这个时分ASP.NET从线程池提取线程并挪用End办法。当End前往以后,ASP.NET实行该页性命周期其他的部分,包含出现阶段。在Begin前往和挪用End之间,该哀求处置线程能够自在地服务于其他哀求,直至挪用End且提早出现为止。因为2.0版的.NETFramework供应多种实行异步操纵的体例,因而,您乃至无需完成IasyncResult。反之,Framework替您完成。
中的代码埋没类供应一个示例。呼应页包括一个ID为“Output”的Label控件。该页利用System.Net.HttpWebRequest类提取http://MSDN.microsoft.com的内容。然后,它剖析前往的HTML,并将它发明的全体HREF方针列表写出到Label控件。
usingSystem;
usingSystem.Web;
usingSystem.Web.UI;
usingSystem.Web.UI.WebControls;
usingSystem.Net;
usingSystem.IO;
usingSystem.Text;
usingSystem.Text.RegularExpressions;publicpartialclassAsyncPage:System.Web.UI.Page
{
privateWebRequest_request;
voidPage_Load(objectsender,EventArgse)
{
AddOnPreRenderCompleteAsync(
newBeginEventHandler(BeginAsyncOperation),
newEndEventHandler(EndAsyncOperation)
);
}
IAsyncResultBeginAsyncOperation(objectsender,EventArgse,
AsyncCallbackcb,objectstate)
{
_request=WebRequest.Create("http://msdn.microsoft.com");
return_request.BeginGetResponse(cb,state);
}
voidEndAsyncOperation(IAsyncResultar)
{
stringtext;
using(WebResponseresponse=_request.EndGetResponse(ar))
{
using(StreamReaderreader=newStreamReader(response.GetResponseStream()))
{
text=reader.ReadToEnd();
}
}
Regexregex=newRegex("hrefs*=s*"([^"]*)"",RegexOptions.IgnoreCase);
MatchCollectionmatches=regex.Matches(text);
StringBuilderbuilder=newStringBuilder(1024);
foreach(Matchmatchinmatches)
{
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呼应前往到扫瞄器。
<Pclass=figureCaption>同步和异步页处置
申明ASP.NET2.0同步和异步页之间的区分。当哀求同步页时,ASP.NET为该哀求分派线程池中的一个线程,并在该线程上实行页。假如该哀求中断实行I/O操纵,则挂起线程,直到完成操纵,从而能够完成该页的性命周期。相反,异步页一般经由过程PreRender事务实行。然后,挪用利用AddOnPreRenderCompleteAsync注册的Begin办法,以后,该哀求处置线程前往线程池。Begin启动一个异步I/O操纵,当该操纵完成时,ASP.NET从线程池提取另外一个线程并挪用End办法,而且在该线程上实行该页性命周期的其他部分。
跟踪输入显现异步页的异步点
对Begin的挪用标志该页的“异步点”。中的跟踪正确显现异步点产生在那边。假如挪用,则必需在异步点之前挪用AddOnPreRenderCompleteAsync―即,不晚于该页的PreRender事务。
<Pclass=figureCaption> 异步数据绑定
一般情形下,ASP.NET页其实不利用HttpWebRequest间接哀求其他页,但它们一般查询数据库并对了局举行数据绑定。因而,您将怎样利用异步页实行异步数据绑定呢?中的代码埋没类显现举行此操纵的一种体例。
usingSystem;
usingSystem.Data;
usingSystem.Data.SqlClient;
usingSystem.Web;
usingSystem.Web.UI;
usingSystem.Web.UI.WebControls;
usingSystem.Web.Configuration;publicpartialclassAsyncDataBind:System.Web.UI.Page
{
privateSqlConnection_connection;
privateSqlCommand_command;
privateSqlDataReader_reader;
protectedvoidPage_Load(objectsender,EventArgse)
{
if(!IsPostBack)
{
//HookPreRenderCompleteeventfordatabinding
this.PreRenderComplete+=newEventHandler(Page_PreRenderComplete);
//Registerasyncmethods
AddOnPreRenderCompleteAsync(
newBeginEventHandler(BeginAsyncOperation),
newEndEventHandler(EndAsyncOperation)
);
}
}
IAsyncResultBeginAsyncOperation(objectsender,EventArgse,AsyncCallbackcb,objectstate)
{
stringconnect=WebConfigurationManager.ConnectionStrings
["PubsConnectionString"].ConnectionString;
_connection=newSqlConnection(connect);
_connection.Open();
_command=newSqlCommand("SELECTtitle_id,title,priceFROMtitles",_connection);
return_command.BeginExecuteReader(cb,state);
}
voidEndAsyncOperation(IAsyncResultar)
{
_reader=_command.EndExecuteReader(ar);
}
protectedvoidPage_PreRenderComplete(objectsender,EventArgse)
{
Output.DataSource=_reader;
Output.DataBind();
}
publicoverridevoidDispose()
{
if(_connection!=null)_connection.Close();
base.Dispose();
}
}
AsyncDataBind.aspx.cs与AsyncPage.aspx.cs利用不异的AddOnPreRenderCompleteAsync形式。可是,AsyncDataBind.aspx.cs的BeginAsyncOperation办法挪用ADO.NET2.0中的新办法SqlCommand.BeginExecuteReader(而非HttpWebRequest.BeginGetResponse),以实行一个异步数据库查询。当挪用完成时,EndAsyncOperation挪用SqlCommand.EndExecuteReader以猎取SqlDataReader,然后将其存储在公有字段中。在用于PreRenderComplete事务(在异步操纵完成但出现该页之前激发)的事务处置程序中,AsyncDataBind.aspx.cs以后将SqlDataReader绑定到OutputGridView控件。从表面上看,该页相似于利用GridView出现数据库查询了局的一般(同步)页。可是在外部,该页更具可伸缩性,由于它其实不挂起线程池线程以守候查询前往。
<Pclass=figureCaption> 异步伐用Web服务
另外一个一般由ASP.NETWeb页实行的、与I/O相干的义务是修改Web服务。因为Web服务挪用消费较长工夫才干前往,因而,实行它们的页是用于异步处置的幻想选择。
usingSystem;
usingSystem.Data;
usingSystem.Configuration;
usingSystem.Web;
usingSystem.Web.UI;
usingSystem.Web.UI.WebControls;publicpartialclassAsyncWSInvoke1:System.Web.UI.Page
{
privateWS.PubsWebService_ws;
privateDataSet_ds;
protectedvoidPage_Load(objectsender,EventArgse)
{
if(!IsPostBack)
{
//HookPreRenderCompleteeventfordatabinding
this.PreRenderComplete+=newEventHandler(Page_PreRenderComplete);
//Registerasyncmethods
AddOnPreRenderCompleteAsync(newBeginEventHandler(BeginAsyncOperation),
newEndEventHandler(EndAsyncOperation)
);
}
}
IAsyncResultBeginAsyncOperation(objectsender,EventArgse,AsyncCallbackcb,objectstate)
{
_ws=newWS.PubsWebService();
//FixupURLforcalltolocalVWD-hostedWebservice
_ws.Url=newUri(Request.Url,"Pubs.asmx").ToString();
_ws.UseDefaultCredentials=true;
return_ws.BeginGetTitles(cb,state);
}
voidEndAsyncOperation(IAsyncResultar)
{
_ds=_ws.EndGetTitles(ar);
}
protectedvoidPage_PreRenderComplete(objectsender,EventArgse)
{
Output.DataSource=_ds;
Output.DataBind();
}
publicoverridevoidDispose()
{
if(_ws!=null)_ws.Dispose();
base.Dispose();
}
}
<Pclass=figureCaption> 显现天生修改Web服务的异步页的体例。它利用和中不异的AddOnPreRenderCompleteAsync机制。该页的Begin办法经由过程挪用Web服务代办署理的异步Begin办法启动一个异步Web服务挪用。该页的End办法在公有字段中缓存对Web办法前往的DataSet的援用,而且PreRenderComplete处置程序将DataSet绑定到GridView。作为参考,该挪用的方针Web办法如以下代码所示:
- [WebMethod]publicDataSetGetTitles(){stringconnect=WebConfigurationManager.ConnectionStrings["PubsConnectionString"].ConnectionString;SqlDataAdapteradapter=newSqlDataAdapter("SELECTtitle_id,title,priceFROMtitles",connect);DataSetds=newDataSet();adapter.Fill(ds);returnds;}
复制代码 这只是个中一种体例,但并非独一的体例。.NETFramework2.0Web服务代办署理撑持两种对Web服务举行异步伐用的机制。一个是.NETFramework1.x和2.0Web服务代办署理中的每办法Begin和End办法。另外一个是仅由.NETFramework2.0的Web服务代办署理供应的新MethodAsync办法和MethodCompleted事务。
假如一个Web服务有一个名为Foo的办法,那末除具着名为Foo、BeginFoo和EndFoo的办法外,.NETFramework版本2.0Web服务代办署理还包含名为FooAsync的办法和名为FooCompleted的事务。能够经由过程注册FooCompleted事务的处置程序并挪用FooAsync来异步伐用Foo,以下所示:- proxy.FooCompleted+=newFooCompletedEventHandler(OnFooCompleted);proxy.FooAsync(...);...voidOnFooCompleted(Objectsource,FooCompletedEventArgse){//CalledwhenFoocompletes}
复制代码 当异步伐用因为FooAsync完成而入手下手时,将激发FooCompleted事务,从而招致挪用FooCompleted事务处置程序。包装该事务处置程序(FooCompletedEventHandler)的托付和传送给它的第二个参数(FooCompletedEventArgs)都随Web服务代办署理一同天生。可经由过程FooCompletedEventArgs.Result会见Foo的前往值。
展现利用MethodAsync形式异步伐用Web服务的GetTitles办法的代码埋没类。从功效上讲,该页同等于中的页。但其外部完成则年夜为分歧。AsyncWSInvoke2.aspx包含一个@PageAsync=“true”指令,相似于AsyncWSInvoke1.aspx。可是,AsyncWSInvoke2.aspx.cs其实不挪用AddOnPreRenderCompleteAsync;它注册一个用于GetTitlesCompleted事务的处置程序,并挪用Web服务代办署理上的GetTitlesAsync。ASP.NET仍旧提早出现该页,直到GetTitlesAsync完成。在外部,当异步伐用入手下手和完成时,它利用System.Threading.SynchronizationContext的一个实例(2.0的一个新类)吸收关照。
usingSystem;
usingSystem.Data;
usingSystem.Configuration;
usingSystem.Web;
usingSystem.Web.UI;
usingSystem.Web.UI.WebControls;publicpartialclassAsyncWSInvoke2:System.Web.UI.Page
{
privateWS.PubsWebService_ws;
privateDataSet_ds;
protectedvoidPage_Load(objectsender,EventArgse)
{
if(!IsPostBack)
{
//HookPreRenderCompleteeventfordatabinding
this.PreRenderComplete+=newEventHandler(Page_PreRenderComplete);
//CalltheWebserviceasynchronously
_ws=newWS.PubsWebService();
_ws.GetTitlesCompleted+=new
WS.GetTitlesCompletedEventHandler(GetTitlesCompleted);
_ws.Url=newUri(Request.Url,"Pubs.asmx").ToString();
_ws.UseDefaultCredentials=true;
_ws.GetTitlesAsync();
}
}
voidGetTitlesCompleted(Objectsource,
WS.GetTitlesCompletedEventArgse)
{
_ds=e.Result;
}
protectedvoidPage_PreRenderComplete(objectsender,EventArgse)
{
Output.DataSource=_ds;
Output.DataBind();
}
publicoverridevoidDispose()
{
if(_ws!=null)_ws.Dispose();
base.Dispose();
}
}
利用MethodAsync而非AddOnPreRenderCompleteAsync完成异步页有两个上风。起首,MethodAsync将摹拟、地区性和HttpContext.Current注进MethodCompleted事务处置程序,而AddOnPreRenderCompleteAsync则否则。其次,假如该页举行多个异步伐用,并且必需提早出现直到一切挪用完成,则利用AddOnPreRenderCompleteAsync请求您天生一个在一切挪用完成前坚持无旌旗灯号形态的IasyncResult。利用MethodAsync,如许的操纵就不是必须的;您只需安排这些挪用(数目不限),ASP.NET引擎提早该出现阶段,直到最初一个挪用前往。
<Pclass=figureCaption> 异步义务
MethodAsync是从异步页举行多个异步Web服务挪用并提早出现阶段直到一切挪用完成的一个烦琐办法。但假如您想在一个异步页中实行多少异步I/O操纵,并且这些操纵不触及Web服务,那该怎样呢?这么说,能够反过去天生一个IAsyncResult,它能够前往到ASP.NET以同意它懂得最初一个挪用什么时候完成的吗?侥幸的是,谜底是不是定的。
在ASP.NET2.0中,System.Web.UI.Page类引进了另外一个办法来简化异步操纵:RegisterAsyncTask。RegisterAsyncTask比AddOnPreRenderCompleteAsync具有四个上风。起首,除Begin和End办法,RegisterAsyncTask还同意您注册当异步操纵长工夫没法完成时挪用的超时办法。您能够经由过程在该页的@Page指令中包括AsyncTimeout属性以声明性体例设置超时。AsyncTimeout="5"将超时设置为5秒。第二个上风是,您能够在一个哀求中屡次挪用RegisterAsyncTask来注册多少异步操纵。和利用MethodAsync一样,ASP.NET提早出现该页,直到一切操纵完成。第三,您可使用RegisterAsyncTask的第四个参数将形态传送给Begin办法。最初,RegisterAsyncTask将摹拟、地区性和HttpContext.Current注进End和Timeout办法。正如本文后面提到的,利用AddOnPreRenderCompleteAsync注册的End办法的情形则否则。
在其他方面,依附于RegisterAsyncTask的异步页与依附于AddOnPreRenderCompleteAsync的异步页相相似。它仍旧必要@Page指令(或等效的编程指令,它会将该页的AsyncMode属性设置为true)中的Async=“true”属性,并且它仍旧与平常一样经由过程PreRender事务实行,此时挪用利用RegisterAsyncTask注册的Begin办法,并且进一步坚持哀求处置直到最初一个操纵完成。比方,中的代码埋没类在功效上与中的等效,可是它利用RegisterTaskAsync而非利用AddOnPreRenderCompleteAsync。请注重名为TimeoutAsyncOperation的超时处置程序,假如HttpWebRequest.BeginGetRequest长工夫没法完成,将挪用该处置程序。响应的.aspx文件包含一个将超工夫隔设置为5秒的AsyncTimeout属性。还请注重传给RegisterAsyncTask的第四个参数(可用于将数据传送到Begin办法)的null。
usingSystem;
usingSystem.Web;
usingSystem.Web.UI;
usingSystem.Web.UI.WebControls;
usingSystem.Net;
usingSystem.IO;
usingSystem.Text;
usingSystem.Text.RegularExpressions;publicpartialclassAsyncPageTask:System.Web.UI.Page
{
privateWebRequest_request;
protectedvoidPage_Load(objectsender,EventArgse)
{
PageAsyncTasktask=newPageAsyncTask(
newBeginEventHandler(BeginAsyncOperation),
newEndEventHandler(EndAsyncOperation),
newEndEventHandler(TimeoutAsyncOperation),
null
);
RegisterAsyncTask(task);
}
IAsyncResultBeginAsyncOperation(objectsender,EventArgse,
AsyncCallbackcb,objectstate)
{
_request=WebRequest.Create("http://msdn.microsoft.com");
return_request.BeginGetResponse(cb,state);
}
voidEndAsyncOperation(IAsyncResultar)
{
stringtext;
using(WebResponseresponse=_request.EndGetResponse(ar))
{
using(StreamReaderreader=newStreamReader(response.GetResponseStream()))
{
text=reader.ReadToEnd();
}
}
Regexregex=newRegex("hrefs*=s*"([^"]*)"",RegexOptions.IgnoreCase);
MatchCollectionmatches=regex.Matches(text);
StringBuilderbuilder=newStringBuilder(1024);
foreach(Matchmatchinmatches)
{
builder.Append(match.Groups[1]);
builder.Append("<br/>");
}
Output.Text=builder.ToString();
}
voidTimeoutAsyncOperation(IAsyncResultar)
{
Output.Text="Datatemporarilyunavailable";
}
}
RegisterAsyncTask的次要上风在于,它同意异步页激发多个异步伐用,并提早出现直到一切挪用完成。它也很好地合用于单个异步伐用,并且它供应了AddOnPreRenderCompleteAsync不具有的超时选项。假如天生一个只举行一个异步伐用的异步页,您可使用AddOnPreRenderCompleteAsync或RegisterAsyncTask。但关于安排两个以上异步伐用的异步页,RegisterAsyncTask极年夜地简化了您的操纵。
因为超时价是每页而非每挪用设置,因而您大概想晓得是不是能改动单个挪用的超时价。复杂的回覆是不是。您能够经由过程以编程体例修正页的AsyncTimeout属性,逐一哀求地变动超时,可是您没法将分歧超时分派给从统一哀求初始化的分歧挪用。
包装它
如今,您已懂得了ASP.NET2.0中异步页的本色。它们期近将推出的ASP.NET版本中十分易于完成,而且其系统布局同意您在一个哀求中批处置多个异步I/O操纵,并提早该页的出现直到一切操纵完成。经由过程与异步ADO.NET和.NETFramework中的其他新异步功效相分离,异步ASP.NET页针对因充斥线程池而限定可伸缩性的I/O绑定哀求成绩供应懂得决计划。
当天生异步页时最初必要注重的一点是,不该该启动来自ASP.NET利用的统一线程池的异步操纵。比方,在页的异步点挪用ThreadPool.QueueUserWorkItem会起反感化,由于该办法来自线程池,从而招致地道猎取用于处置哀求的零线程。相反,挪用内置于Framework中的异步办法(比方,办法HttpWebRequest.BeginGetResponse和SqlCommand.BeginExecuteReader)一般以为是平安的,由于这些办法偏向于利用完成端话柄现异步举动。
来吧!老师们!我代表千千万万的asp.net/C#的初学者在这里呼唤着! |
|