ASP.NET网页设计简化异步操纵(上):利用CCR和AsyncEnumerator简化异步操纵仓酷云 ...
刚刚打开这篇专题,猛然见到HAL9000发表的《对于大型公司项目平台选择j2ee的几层认识》系列,深受启发。在之前的文章中,我曾屡次夸大使用程序中异步化的主要性。特别关于IO麋集型操纵来讲,异步实行关于使用程序的呼应才能和伸缩性有十分关头的影响。准确利用异步编程可以利用尽量少的线程来实行大批的IO麋集型操纵。惋惜的是,即便异步编程有制止线程堵塞等诸多优点,可是这类编程体例至今没有被大批接纳。其缘故原由有良多,个中最次要的一点大概就是异步模子在编程上较为坚苦,招致很多开辟职员不肯意往做。异步,则意味着一个义务最少要被拆分为“二段式”的挪用体例:一个办法用于倡议异步哀求,另外一个办法用于异步义务完成后的回调。与传统办法的挪用方式比拟,异步伐用时的两头数据不克不及寄存在线程栈上,办法之间的也不克不及复杂地经由过程参数传送的体例来共享数据。别的,传统办法挪用中可以使用的try…catch…finally,using等关头字都没法超过办法界限,因而异步编程在处置非常,回护资本等方面也必要花更年夜的精神才行。假如一不当心,轻则形成资本保守,重则使全部使用程序溃散。
因而,不管是微软官方仍是社区中都呈现了一些简化异步编程体例的组件,比方微软的CCR和Wintellect"s.NETPowerThreadingLibrary中的AsyncEnumerator。可是它们都有一样的范围性,比方操纵之间存在依附,则很难让它们并行实行。关于如许的场景,我们还需要构建分外的办理计划,使多个有依附干系的异步操纵之间的合作挪用得以尽量的简化。
传统异步操纵利用体例
.NET平台中的异步编程体例为APM(AsynchronousProgrammingModel,异步编程模子)形式。它利用BeginXxx和EndXxx两个办法构成了二段式挪用,而且经由过程回调函数(AsyncCallback)和异步形态(IAsyncResult)举行合作完成全部异步操纵。比方,假如我们要异步读取一个文件的内容,我们大概会利用这类办法:
FileStreamfs=newFileStream(@"C:Sample.data",FileMode.Open,FileAccess.Read,FileShare.Read,8192,FileOptions.Asynchronous);Byte[]data=newByte;fs.BeginRead(data,0,data.Length,result=>{Int32bytesRead=fs.EndRead(result);ProcessData(data);//处置数据fs.Close();//封闭文件流},null);在这段代码中,经由过程挪用FileStream对象的BeginRead办法来倡议一个异步的读取操纵,而且利用Lambda表达式机关一个匿名AsyncCallback对象。AsyncCallback回调函数将在异步读取完成以后由框架挪用,个中会实行FileSystem对象的EndRead办法来猎取异步操纵的了局。最初,还必需显式地封闭文件流,而不克不及利用using关头字来举行帮助,这是因为文件流在倡议异步操纵之前打开,而在回调函数中才干被封闭。在下面的代码中另有一个关头,那就是使用了.NET2.0中的匿名函数特征构成了一个闭包。因为闭包外部也能够会见内部办法的部分变量(比方AsyncCallback回调函数会见了fs对象),在必定程度使得“二段式”的异步伐用模子共享了“栈”上的数据——实在编译器已将必要共享的字段和匿名函数放进了一个托管堆上的帮助对象里了。
从下面的代码看来,利用一个异步操纵其实不如设想中的坚苦,可是在实践临盆中几近不会呈现云云复杂的场景。一个对照罕见的场景是:在从一个数据源中获取了一篇文章的一切批评以后,必要依据每条批评的用户ID往另外一个数据源猎取用户信息,再加以组兼并显现给用户检察。关于下层使用(如本例中的UI层)来说,这两个异步操纵为一个全体,只要两个异步操纵完成后,一个完全的异步操纵才算停止。这类组合常常会使代码进进一种匿名函数相互嵌套的干系:
//倡议GetComment异步操纵Service.BeginGetComments(1,//对象IDcommentAsyncResult=>//GetComments操纵的回调函数{//GetComments操纵完成,取得一个IEnumerable<Comment>对象varcomments=Service.EndGetComments(commentAsyncResult);//倡议GetUsers异步操纵Service.BeginGetUsers(comments.Select(c=>c.UserID),//失掉用户IDuserAsyncResult=>//GetUsers操纵的回调函数{//GetUsers操纵完成varusers=Service.EndGetUsers(userAsyncResult);//处置数据ProcessData(comments,users);},null);},null);依据使用的庞大水平分歧,异步操纵的数目会愈来愈多,假如一味地举行嵌套,代码的保护性将会愈来愈差。可是假如您为了不嵌套而把办法拆开,那末在分离在遍地的回调函数间共享或传送数据又会成为新的成绩。因而,不管是微软官方仍是社区中都呈现了一些组件用于简化异步编程模子的利用。个中最出名的大概就是微软的“并行与和谐运转时”和Wintellect"s.NETPowerThreadingLibrary中的AsyncEnumerator。
利用CCR简化异步编程模子的利用
并行与和谐运转时(ConcurrencyandCoordinationRuntime,CCR)是微软面向呆板人平台开辟的一套框架,包括在MicrosoftRoboticsDeveloperStudio中。MicrosoftRoboticsDeveloperStudio2008ExpressionEdition不同意再次分发,可是能够收费用于贸易和非贸易的目标,您能够在这里浏览它的受权协定。
CCR固然源于呆板人平台,可是它实际上是一个轻量级的基于动静传送机制的编程框架。在CCR所构建的“动静——端口——行列”的处置模子中,几近不会构成任何的线程堵塞,因而能够被用于各类必要高度并发的场景中,您能够在它的官方站点和Channel9上取得它的各类案例。有关CCR的材料其实不多,可是在它的用户手册中提到了基于迭代器(Iterator)的异步开辟体例。
C#2.0中引进了yield关头字,开辟职员能够使用这个新特征轻松完成编写一个迭代器。比方以下代码即是选择出数组中一切年夜于5的元素:
staticIEnumerator<int>Get(int[]array){foreach(varainarray){if(a>5)yieldreturna;}}编译器在这里又一次年夜显神威,它会依据如上寥寥数行代码主动构建一个庞大的,上百行代码的IEnumerator<int>对象,完成了Reset,Current,MoveNext,Dispose等多个成员。在挪用一次迭代器的MoveNext的办法以后,代码将在yieldreturn语句的地方前往(假定在没有其他加入的情形下),直到下次MoveNext办法被挪用时才接着之前yieldreturn语句的下一行持续实行。
一样平常来讲,我们会利用C#的foreach关头字来遍历一个迭代器中的一切元素,它会主动天生关于MoveNext/Current成员的挪用。但是如今,yield特征被奇妙地用在了异步编程模子上。试想,开辟职员能够在迭代器中倡议一个异步操纵,以后当即利用yieldreturn语句前往,而且经由过程某个机制在异步操纵停止以后(比方使用异步操纵的AsyncCallback回调函数)再次挪用迭代器的MoveNext办法,接着方才的逻辑持续实行。经由过程这类“倡议操纵——yieldreturn——完成操纵——倡议下一次操纵——yieldreturn——完成下一次操纵……”的体例,我们可使用靠近于传统的开辟体例来举行异步操纵。我们如今利用CCRIterator的体例,将之前异步猎取批评和用户的代码举行改写:
staticIEnumerator<ITask>GetEnumerator(){varresultPort=newPort<IAsyncResult>();//倡议GetComments异步操纵Service.BeginGetComments(1,resultPort.Post,null);//中止,守候GetComments操纵完成yieldreturnresultPort.Receive();//GetComments操纵完成,猎取了局varcomments=Service.EndGetComments((IAsyncResult)resultPort.Test());//倡议GetUsers异步操纵Service.BeginGetUsers(comments.Select(c=>c.UserID),resultPort.Post,null);//中止,守候GetUsers操纵完成yieldreturnresultPort.Receive();//GetUsers操纵完成,猎取了局varusers=Service.EndGetUsers((IAsyncResult)resultPort.Test());//处置数据ProcessData(comments,users);}然后,我们可使用以下体例挪用这个迭代器:
Dispatcherdispatcher=newDispatcher();DispatcherQueuequeue=newDispatcherQueue("readqueue",dispatcher);Arbiter.Activate(queue,Arbiter.FromIteratorHandler(CreateEnumerator));利用如许体例来实行异步操纵,不但免除层层嵌套之苦,更在于它真真正正地利用了传统的开辟体例——这意味着之前所谈到的各类缺点,比方没法利用try…catch…finally和using的成绩都不复存在了。异步天下一会儿优美了很多。固然,CCR的功效远不止云云,这里只是利用它的一小部分功效罢了,感乐趣的伴侣们能够往之前给出的链接中更进一步懂得CCR的壮大功效。
利用AsyncEnumerator简化异步模子的利用
Wintellect"s.NETPowerThreadingLibrary是由JeffreyRichter开发的一套类库,包括了很多与多线程和异步编程相干的组件,而AsyncEnumerator则是个中之一。AsyncEnumerator关于异步编程模型的撑持,在道理上与CCR不异,可是因为它是间接面向这类异步机制的帮助,因而在功效上加倍完美,利用也较为便利。比方,之前的例子能够改写为:
staticIEnumerator<int>GetEnumerator(AsyncEnumeratorenumerator){//倡议GetComments异步操纵Service.BeginGetComments(1,enumerator.End(),null);//中止,守候GetComments操纵完成yieldreturn1;//GetComments操纵完成,猎取了局varcomments=Service.EndGetComments(enumerator.DequeueAsyncResult());//倡议GetUsers异步操纵Service.BeginGetUsers(comments.Select(c=>c.UserID),enumerator.End(),null);//中止,守候GetUsers操纵完成yieldreturn1;//GetUsers操纵完成,猎取了局varusers=Service.EndGetUsers(enumerator.DequeueAsyncResult());//处置数据ProcessData(comments,users);}在利用时,开辟职员必要机关一个IEnumerator<int>对象来指引AsyncEnumerator的调剂。AsyncEnumerator的End办法会前往一个AsyncCallback对象,必要交给每一个倡议异步操纵的办法,用于在一个异步操纵完成时举行关照。在AsyncEnumerator中会保护一个行列,某个异步操纵完成后,它的IAsyncResult对象就会放进这个行列中,而DequeueAsyncResult办法即可将IAsyncResult对象从行列中掏出。每次yieldreturn的值,则标明必要等AsyncEnumerator中存在“几个”未出行列的IAsyncResult对象才持续实行下一行代码。使用这个特征,我们能够在yieldreturn语句之前倡议多个异步操纵,而且利用一句yieldreturn来“守候”多个异步操纵完成。比方在以下的代码中,只要在一切异步操纵(即一切的GetResponse操纵)完成以后才干从yieldreturn的下一条语句入手下手持续实行:
staticIEnumerator<int>GetEnumerator(AsyncEnumeratorenumerator,IEnumerable<string>urls){intcount=0;foreach(stringurlinurls){count++;WebRequestrequest=HttpWebRequest.Create(url);request.BeginGetResponse(enumerator.End(),request);}yieldreturncount;for(inti=0;i<count;i++){IAsyncResultasyncResult=enumerator.DequeueAsyncResult();WebRequestrequest=(WebRequest)asyncResult.AsyncState;WebResponseresponse=request.EndGetResponse(asyncResult);ProcessResponse(response);}}在构建完IEnumerator以后,您可使用AsyncEnumerator的Execute办法实行全部异步操纵:
AsyncEnumeratorasyncEnumerator=newAsyncEnumerator();asyncEnumerator.Execute(GetEnumerator(asyncEnumerator,...));不外Execute办法会堵塞挪用线程,因而,AsyncEnumerator也一样供应了BeginExecute和EndExecute办法构成了一个尺度的APM形式。
假如您但愿对AsyncEnumerator有更多懂得,能够参考JeffreyRichter在MSDNMagazine上的ConcurrentAffairs专栏里的文章:《SimplifiedAPMwithC#》、《SimplifiedAPMwiththeAsyncEnumerator》和《MoreAsyncEnumeratorFeatures》。
CCR或AsyncEnumerator的范围
有了CCR或AsyncEnumerator的撑持,开辟由多个异步操纵组合而成的异步伐用并不是难事,由于如今的异步开辟从编码体例上就已与一般的办法十分靠近了。不管从逻辑把持仍是资本办理,都可使用传统的手腕举行开辟,异步操纵仿佛历来没有那末简单过。可是光靠如许的帮助其实不可以在某些场景下失掉最好的办理计划。试想您在开辟一个ASP.NET页面用于展现一篇文章,个中必要显现各类信息:
[*]文章内容
[*]批评信息
[*]对批评内容举行打分的用户
[*]打分者的保藏
因为程序架构的缘故原由,数据必要从各个分歧服务或数据源中猎取(这是个很罕见的情形)。因而,程序中已筹办了以下的数据读取接口:
[*]Begin/EndGetContent:依据文章ID(Int32),猎取文章内容(String)
[*]Begin/EndGetComments:依据文章ID(Int32),猎取一切批评(IEnumerable<Comment>)
[*]Begin/EndGetUsers:依据多个用户ID(IEnumerable<int>),猎取一批用户(Dictionary<int,User>)
[*]Begin/EndGetCommentRaters:依据多个批评ID(IEnumerable<int>),猎取一切打分者(IEnumerable<User>)
[*]Begin/EndGetFavorites:依据多个用户ID(IEnumerable<int>),猎取一切保藏(IEnumerable<string>)
假如利用AsyncEnumerator帮助开辟,您大概会写出以下的代码:
privateIEnumerator<int>GetSerialEnumerator(AsyncEnumeratorenumerator,intarticleId){//猎取文章内容Service.BeginGetContent(articleId,enumerator.End(),null);yieldreturn1;this.Content=Service.EndGetContent(enumerator.DequeueAsyncResult());//猎取批评Service.BeginGetComments(articleId,enumerator.End(),null);yieldreturn1;varcomments=Service.EndGetComments(enumerator.DequeueAsyncResult());//猎取批评者信息,并分离批评绑定至控件Service.BeginGetUsers(comments.Select(c=>c.UserID),enumerator.End(),null);yieldreturn1;varusers=Service.EndGetUsers(enumerator.DequeueAsyncResult());this.rptComments.DataSource=fromcincommentsselectnew{Comment=c,User=users};this.rptComments.DataBind();//猎取批评的打分者,并绑定至控件Service.BeginGetCommentRaters(comments.Select(c=>c.CommentID),enumerator.End(),null);yieldreturn1;varraters=Service.EndGetCommentRaters(enumerator.DequeueAsyncResult());this.rptRaters.DataSource=raters;this.rptRaters.DataBind();//猎取打分者的保藏,并绑定至控件Service.BeginGetFavorites(raters.Select(u=>u.UserID),enumerator.End(),null);yieldreturn1;this.rptFavorites.DataSource=Service.EndGetFavorites(enumerator.DequeueAsyncResult());this.rptFavorites.DataBind();}仿佛统统一般,不是吗?为了发明成绩,我们做一个略为夸大的假定:“每一个操纵城市利用2秒钟完成才干完成”,而且利用表示图来体现一切操纵的运转时段:
<br>
固然,因为充实地而且公道天时用了异步操纵,因而在全部实行过程当中只要极小部分工夫才会占用线程举行运算。可是,信任您也已发明了成绩:因为一切操纵都是串行的,因而统共必要10秒钟工夫才干完玉成部操纵。这年夜可不用。招致一切操纵串行的缘故原由常常是它们的之间存在着的依附干系(天然也有多是其他缘故原由,比方“资本的合作”,可是我们这里临时不思索这些要素),在我们的示例中最分明的依附干系,即是一个操纵的输入将作为另外一个操纵的输出。假如我们将这类干系绘制成图示,那末操纵之间的依附便一览无余了:
<br>
这五个操纵恰好能够分为三个阶段,个中A和B,C和D都可同时运转。因而,在幻想情形下,五个操纵的实行阶段应当以下图所示:
<br>
在资本充分的情形下,并行的功能常常优于串行,这是不争的现实,可是要做到这一点实在其实不简单。CCR或AsyncEnumerator的上风在于把各别步操纵利用一般编程体例串联了起来,因而我们才干利用try…catch…finally和using等关头字来简化我们的逻辑完成。假如一旦请求并行,那用传统的编程体例则又没法完成了。假如看了之前的内容,您大概会以为利用AsyncEnumerator也能够完成并行,只需“在yieldreturn语句之前倡议多个异步操纵”不就能够了吗?实在否则,由于不管是CCR仍是AsyncEnumerator都有个“硬伤”:在猎取一个IAsyncResult对象以后,必需由开辟职员来指定这个对象的回属成绩,如许才干将它交由符合的End办法来完成一个异步操纵。在“串行的异步”中做到这点其实不坚苦,由于yieldreturn语句后取得的IAsyncResult对象一定属于之前倡议的异步操纵。假如在“并行”的情形下,则必要经由过程分外的机制来坚持这类异步干系。
比方,在之前WebRequest的示例中,我们利用asyncState来保留IAsyncResult对象所对应的WebRequest。可是我们这么做必要一个条件:并行的操纵完整不异,只是从分歧的对象倡议。也只要云云,才干闪开发职员断定IAsyncRequst对象的操纵体例,不然烦琐的if…else无可制止。就拿上例五个异步操纵来讲,固然操纵A一定比操纵E要提早入手下手,可是我们极可能没法包管A比E要提早完成。别的,一个异步操纵大概会依附于其他多个异步操纵,因而一个异步操纵完成以后,其实不能申明依附于它的异步操纵已可以入手下手。我们几近能够一定,间接利用AsyncEnumerator会编写出凌乱而难以保护的“并行”代码。
鄙人一片文章中,我们将构建一个组件来办理这方面的成绩,使多个有依附干系的异步操纵之间的合作挪用得以尽量的简化。
总结
关于IO麋集型操纵来讲,异步实行关于使用程序的呼应才能和伸缩性有十分关头的影响。准确利用异步编程可以利用尽量少的线程来实行大批的IO麋集型操纵。惋惜的是异步模子在编程上较为坚苦,招致很多开辟职员不肯意往做。微软推出的CCR,和Wintellect"s.NETPowerThreadingLibrary中的AsyncEnumerator都可以在必定水平上简化异步程序的开辟。不外,现有的帮助还不敷以面临一些庞大的场景。比方,要使多个有依附的异步操纵尽量的“并行”,我们还必要构建分外的办理计划。
本文出自:http://www.infoq.com/cn/articles/ccr-async-parti
我也不知道,我原来理解的,NET就是C++编程,只是与net网页编程相对,呵呵。以为.ET就是高级C++编程。 目前在微软的.net战略中新推出的ASP.net借鉴了Java技术的优点,使用CSharp(C#)语言作为ASP.net的推荐语言,同时改进了以前ASP的安全性差等缺点。但是,使用ASP/ASP.net仍有一定的局限性,因为从某种角度来说它们只能在微软的WindowsNT/2000/XP+IIS的服务器平台上良好运行(虽然像ChilliSoft提供了在UNIX/Linux上运行ASP的解决方案. 代码逻辑混乱,难于管理:由于ASP是脚本语言混合html编程,所以你很难看清代码的逻辑关系,并且随着程序的复杂性增加,使得代码的管理十分困难,甚至超出一个程序员所能达到的管理能力,从而造成出错或这样那样的问题。 目前在微软的.net战略中新推出的ASP.net借鉴了Java技术的优点,使用CSharp(C#)语言作为ASP.net的推荐语言,同时改进了以前ASP的安全性差等缺点。但是,使用ASP/ASP.net仍有一定的局限性,因为从某种角度来说它们只能在微软的WindowsNT/2000/XP+IIS的服务器平台上良好运行(虽然像ChilliSoft提供了在UNIX/Linux上运行ASP的解决方案. CGI程序在运行的时候,首先是客户向服务器上的CGI程序发送一个请求,服务器接收到客户的请求后,就会打开一个新的Process(进程)来执行CGI程序,处理客户的请求。CGI程序最后将执行的结果(HTML页面代码)传回给客户。 ASP.net的服务器,要求安装一个.net环境,当然我这里指的是windows系统,顺便点一下,.net只能放在windows环境里来运行。Asp.net1.1的就装Framework1.1,Asp.net2.0的就装Framework2.0。 JSP/Servlet虽然在国内目前的应用并不广泛,但是其前途不可限量。 我的意思是.net好用,从功能上来说比JAVA强还是很明显的。 如今主流的Web服务器软件主要由IIS或Apache组成。IIS支持ASP且只能运行在Windows平台下,Apache支持PHP,CGI,JSP且可运行于多种平台,虽然Apache是世界使用排名第一的Web服务器平台。 碰到复杂点的问题都不知道能不能解决,现在有点实力的公司都选择自已在开源的基础上做开发。但没听说过有人在IIS上做改进的,windows、sqlserver集群方面的应用也很少见。 大哥拜托,Java在95年就出来了,微软垄断个妹啊,服务器市场微软完全是后后来者,当年都是Unix的市场,现在被WindowsServer和Linux抢下大片,包括数据库也一样。 ASP.Net摆脱了以前ASP使用脚本语言来编程的缺点,理论上可以使用任何编程语言包括C++,VB,JS等等,当然,最合适的编程语言还是MS为.NetFrmaework专门推出的C(读csharp)。
页:
[1]