asp.net如何实现异步编程

asp教程.net如何实现异步编程
对于很多人来说,异步就是使用后台线程运行耗时的操作。在有些时候这是对的,而在我们日常大部分场景中却不对。

比如现在我们有这么一个需求:使用httpwebrequest请求某个指定uri的内容,然后输出在界面上的文本域中。同步代码很容易编写:

   1: private void btndownload_click(object sender,eventargs e)   2: {   3:     var request = httpwebrequest.create("http://www.sina.com.cn");   4:     var response = request.getresponse();   5:     var stream = response.getresponsestream();   6:     using(streamreader reader = new streamreader(stream))   7:     {   8:         var content = reader.readtoend();   9:         this.txtcontent.text = content;  10:     }  11: }

是吧,很简单。但是正如上一篇文章所说,这个简短的程序体验会非常差。特别是在uri所指向的资源非常大,网络非常慢的情况下,在点击下载按钮到获得结果这段时间界面会假死。

哦,这个时候你想起了异步。回忆上篇文章的示意图。我们发现只要我们将耗时的操作放到另外一个线程上执行就可以了,这样我们的ui线程可以继续响应用户的操作。

使用独立的线程实现异步
如是你写下了下面的代码:

 

  1: private void btndownload_click(object sender,eventargs e)   2: {   3:     var downloadthread = new thread(download);   4:     downloadthread.start();   5: }   6:     7: private void download()   8: {   9:     var request = httpwebrequest.create("http://www.sina.com.cn");  10:     var response = request.getresponse();  11:     var stream = response.getresponsestream();  12:     using(streamreader reader = new streamreader(stream))  13:     {  14:         var content = reader.readtoend();  15:         this.txtcontent.text = content;  16:     }  17: }

然后,f5运行。很不幸,这里出现了异常:我们不能在一个非ui线程上更新ui的属性(更详细的讨论参见我的这篇文章:winform二三事(三)control.invoke&control.begininvoke)。我们暂时忽略这个异常(在release模式下是不会出现的,但这是不推荐的做法)。

哦,你写完上面的代码后发现ui不再阻塞了。心里想,异步也不过如此嘛。过了一会儿你突然想起,你好像在哪本书里看到过说尽量不要自己声明thread,而应用使用线程池。如是你搜索了一下msdn,将上面的代码改成下面这个样子:

  

1: private void btndownload_click(object sender,eventargs e)   2: {   3:     threadpool.queueuserworkitem(download);       4: }   5:     6: private void download()   7: {   8:     var request = httpwebrequest.create("http://www.sina.com.cn");   9:     var response = request.getresponse();  10:     var stream = response.getresponsestream();  11:     using(streamreader reader = new streamreader(stream))  12:     {  13:         var content = reader.readtoend();  14:         this.txtcontent.text = content;  15:     }  16: }
 

嗯,很容易完成了。你都有点佩服自己了,这么短的时间居然连线程池这么“高级的技术”都给使用上了。就在你沾沾自喜的时候,你的一个同事走过来说:你这种实现方式是非常低效的,这里要进行的耗时操作属于io操作,不是计算密集型,可以不分配线程给它(虽然不算准确,但如果不深究的话就这么认为吧)。

你的同事说的是对的。对于io操作(比如读写磁盘,网络传输,数据库教程查询等),我们是不需要占用一个thread来执行的。现代的磁盘等设备,都可以与cpu同时工作,在磁盘寻道读取这段时间cpu可以干其他的事情,当读取完毕之后通过中断再让cpu参与进来。所以上面的代码,虽然构建了响应灵敏的界面,但是却创建了一个什么也不干的线程(当进行网络请求这段时间内,该线程会被一直阻塞)。所以,如果你要进行异步时首先要考虑,耗时的操作属于计算密集型还是io密集型,不同的操作需要采用不同的策略。对于计算密集型的操作你是可以采用上面的方法的:比如你要进行很复杂的方程的求解。是采用专门的线程还是使用线程池,也要看你的操作的关键程度。

这个时候你又在思考,不让我使用线程,又要让我实现异步。这该怎么办呢?微软早就帮你想到了这点,在.net framework中,几乎所有进行io操作的方法几乎都提供了同步版本和异步版本,而且微软为了简化异步的使用难度还定义了两种异步编程模式:

classic async pattern
这种方式就是提供两个方法实现异步编程:比如system.io.stream的read方法:

public int read(byte[] buffer,int offset,int count);

它还提供了两个方法实现异步读取:

public iasyncresult beginread(byte[] buffer, int offset,int count,asynccallback callback);

public int endread(iasyncresult asyncresult);

以begin开头的方法发起异步操作,begin开头的方法里还会接收一个asynccallback类型的回调,该方法会在异步操作完成后执行。然后我们可以通过调用endread获得异步操作的结果。关于这种模式更详细的细节我不在这里多阐述,感兴趣的同学可以阅读《clr via c#》26、27章,以及《.net设计规范》里对异步模式的描述。在这里我会使用这种模式重新实现上面的代码片段:

   1: private static readonly int buffer_length = 1024;   2:     3: private void btndownload_click(object sender,eventargs e)   4: {   5:     var request = httpwebrequest.create("http://www.sina.com.cn");   6:     request.begingetresponse((ar) => {   7:         var response = request.endrequest(ar);   8:         var stream = response.getresponsestream();   9:         readhelper(stream,0);  10:     },null);  11: }  12:    13: private void readhelper(stream stream,int offset)  14: {  15:     var buffer = new byte[buffer_length];  16:     stream.beginread(buffer,offset,buffer_length,(ar) =>{  17:         var actualread = stream.endread(ar);  18:           19:         if(actualread == buffer_length)  20:         {  21:             var partialcontent = encoding.default.getstring(buffer);  22:             update(partialcontent);  23:             readhelper(stream,offset+buffer_length);  24:         }  25:         else  26:         {  27:             var latestcontent = encoding.default.getstring(buffer,0,actualread);  28:             update(latestcontent);  29:             stream.close();  30:         }  31:     },null);  32: }  33:    34: private void update(string content)  35: {  36:     this.begininvoke(new action(()=>{this.txtcontent.text += content;}));  37: }
感谢lambda表达式,让我少些了很多方法声明,也少引入了很多实例成员。不过上面的代码还是非常难以读懂,原本简简单单的同步代码被改写成了分段式的,而且我们再也无法使用using了,所以需要显示的写stream.close()。哦,我的代码还没有进行异常处理,这令我非常头痛。实际上要写出一个健壮的异步代码是非常困难的,而且非常难以调试。但是,上面的代码不仅仅能创建响应灵敏的界面,还能更高效的利用线程。在这种异步模式中,beginxxx方法会返回一个iasyncresult对象,在进行异步编程时也非常有效,关于它的更详细信息你可以阅读我的这篇文章:winform二三事(二)异步操作。

除此之外,因为我们在这里不能使用while等循环,我们想要从stream里读取完整的内容并不是一件容易事儿,我们必须将很好的循环结果替换成递归调用:readhelper。

event-based async pattern(eap)
.net framework除了提供上面这种编程模式外,还提供了基于事件的异步编程模式。比如webclient的很多方法就提供了异步版本,比如downloadstring方法。

同步版本:

public string downloadstring(string url);

异步版本:

public void downloadstringasync(string url);

public event downloadstringcompleteeventhandler downloadstringcomplete;

(在这里请注意,这两种异步编程模式以及未来要介绍的async ctp中的tap方法的命名,参数的传递都是有一定规则的,弄清楚这些规则在进行异步编程时会事半功倍)

 

时间: 2024-09-16 22:32:21

asp.net如何实现异步编程的相关文章

对 ASP.NET 异步编程的一点理解

本来这篇博文想探讨下异步中的异常操作,但自己在做异步测试的时候,又对 ASP.NET 异步有了新的认识,可以说自己之前对异步的理解还是有些问题,先列一下这篇博文的三个解惑点: async await 到底是什么鬼??? 异步操作中发生异常,该如何处理? 异步操作中发生异常(有无 catch throw 情况),Application_Error 会不会捕获? 之前测试过异步中的同步(很多种情况),这次我们把测试代码写更复杂些(异步中再进行异步),代码如下: [Route("")]  [

Asp.Net异步编程知识

写在前面的话,很久没有写Blog了,不对,其实一致就没有怎么写过.今天有空,我也来写一篇Blog 随着.Net4.5的推出,一种新的编程方式简化了异步编程,在网上时不时的也看到各种打着Asp.Net异步编程的口号,如何提高性能,如何提高吞吐率! 好多文章都说得不清楚,甚至是错误的.只看到了一些表现,混淆概念.希望这篇文章能够能够对一部分人理解Asp.net异步编程模型. 1基础知识,谈一个初学者不容易理解的基础知识,这个基础知识,很不基础的哦 先看这个代码 ThreadPool.GetMaxTh

C#编程总结(六)异步编程

1.什么是异步? 异步操作通常用于执行完成时间可能较长的任务,如打开大文件.连接远程 计算机或查询数据库.异步操作在主应用程序线程以外的线程中执行.应用程序调用方法异步执行某个 操作时,应用程序可在异步方法执行其任务时继续执行. 2.同步与异步的区别 同步 (Synchronous):在执行某个操作时,应用程序必须等待该操作执行完成后才能继续执行. 异 步(Asynchronous):在执行某个操作时,应用程序可在异步操作执行时继续执行.实质:异步操作, 启动了新的线程,主线程与方法线程并行执行

async And await异步编程活用基础

原文:async And await异步编程活用基础 好久没写博客了,时隔5个月,奉上一篇精心准备的文章,希望大家能有所收获,对async 和 await 的理解有更深一层的理解. async 和 await 有你不知道的秘密,微软会告诉你吗? 我用我自己的例子,去一步步诠释这个技术,看下去,你绝对会有收获.(渐进描述方式,愿适应所有层次的程序员) 从零开始, 控制台 Hello World: 什么?开玩笑吧?拿异步做Hello World?? 下面这个例子,输出什么?猜猜? 1 static

如何正确运用异步编程技术

一.什么是同步和异步? 同步(英语:Synchronization),指对在一个系统中所发生的事件(event)之间进行协调,在时间上出现一致性与统一化的现象.说白了就是多个任务一个一个执行,同一时刻只有一个任务在执行. 异步(英语:Asynchronization),指的是让CPU暂时搁置当前请求的响应,处理下一个请求,当通过轮询或其他方式得到回调通知后,开始运行.多线程将异步操作放入另一线程中运行,通过轮询或回调方法得到完成通知,但是完成端口,由操作系统接管异步操作的调度,通过硬件中断,在完

JavaScript 异步编程

异步编程 Async JavaScript 在 Node 面前获得前所未有的重视.本文结合 Trevor Burnham 所著 <Async JavaScript Build More Responsive Apps with Less Code(中文名: JavaScript 异步编程:设计快速响应的网络应用)>一书,梳理 JavaScript 的异步编程的方方面面. 为更好地了解异步开发的来龙去脉,我们先回顾一下 JS 服务端的历史,看到底解决了什么问题(当然是否真的解决是另外一个问题),

Javascript异步编程的4种方法

你可能知道,Javascript语言的执行环境是"单线程"(single thread). 所谓"单线程",就是指一次只能完成一件任务.如果有多个任务,就必须排队,前面一个任务完成,再执行后面一个任务,以此类推. 这种模式的好处是实现起来比较简单,执行环境相对单纯:坏处是只要有一个任务耗时很长,后面的任务都必须排队等着,会拖延整个程序的执行.常见的浏 览器无响应(假死),往往就是因为某一段Javascript代码长时间运行(比如死循环),导致整个页面卡在这个地方,其

如何设计一门编程语言(八) 异步编程和CPS变换

关于这个话题,其实在(六)里面已经讨论了一半了.学过Haskell的都知道,这个世界上很多东西都可以用monad和comonad来把一些复杂的代码给抽象成简单的.一看就懂的形式.他们的区别,就像用js做一个复杂的带着几层循环的动画,直接写出来和用jquery的"回调"写出来的代码一样.前者能看不能用,后者能用不能看.那有没有什么又能用又能看的呢?我目前只能在Haskell.C#和F#里面看到.至于说为什么,当然是因为他们都支持了monad和comonad.只不过C#作为一门不把&quo

Generator与异步编程概述

在<深入浅出Node.js>的第4章里,笔者深入地介绍了当前盛行在Node和前端JavaScript中的几种异步编程的解决方案,唯独对Generator的解决方案没有介绍.但随着Node版本的升级和ECMAScript harmony的特性不断得到支持,在0.11版本中,我们可以通过启用--harmory参数让V8支持Generator.最近Connect/Express背后的开发团队也将精力转移到新的库和框架上,这个核心库和框架就是co和koa,它们最主要的特点主要就是基于ECMAScrip