APM异步编程模型的优势
2012-08-21 14:11 xiashengwang 阅读(1279) 评论(2) 编辑 收藏 举报我们之所以要花大力气学习APM,就必须要清楚它能解决实际编程中的那些难题。以及现有的技术为什么不行。
简单点说:APM是基于IAsyncResult接口的,采用的BeginXXX和EndXXX的形式来实现异步。
下面这几点就是APM的优势:
1,线程执行是异步的,不会阻塞调用线程。这一点也是我们使用异步的主要目的,我们不就是希望后台处理一些耗时操作吗?
2,任务完成可以得到通知。(非阻塞)
3,任务可以实现进度报告。(需要额外的辅助代码支持)
4,可以有返回值。
5,参数可以是多个。
再看看Thread,ThreadPool,Task能实现上面的几点?这三个中,Task是相对完善的一个,对于第2点它可以通过ContineWith方法来实现,对于第5点,它可以通过Lamda表达式的参数捕获来实现。但总觉得有点怪怪的,没有APM灵活。而对于Thread,ThreadPool来说,除了第1点,其它的都不行。
1,FCL中提供了大量现成的,基于I/O的异步操作实现类。
如,Stream,WebRequest,Dns,SqlCommand等等。对于开发者来说,直接使用它们就可以了。
WebRequest webRequest = WebRequest.Create("http://www.baidu.com"); webRequest.BeginGetResponse(result => { WebResponse webResponse = null; try { webResponse = webRequest.EndGetResponse(result); using (System.IO.FileStream fs = new System.IO.FileStream("D:baidu.html", System.IO.FileMode.Create, System.IO.FileAccess.Write)) { using (System.IO.Stream webStream = webResponse.GetResponseStream()) { webStream.CopyTo(fs); } } } catch (WebException ex) { Console.WriteLine("Failed:" + ex.GetBaseException().Message); } finally { if (webResponse != null) webResponse.Close(); } }, null);
2,将IAsyncResutl转成Task来完成。也是上面的代码的不同版本。
这里就是用TaskFactroy的FromAsync方法将IAsyncResutl转成了Task,你肯定觉得这是多余的,但是Task的优势在于它可以有后续Task,可以按顺序执行,在某些场合,这个功能是非常有用的。
WebRequest webRequest = WebRequest.Create("http://www.baidu.com"); TaskFactory factroy = new TaskFactory(); Task<WebResponse> task = factroy.FromAsync<WebResponse>(webRequest.BeginGetResponse, webRequest.EndGetResponse, TaskCreationOptions.None); task.ContinueWith(t => { WebResponse webResponse = null; try { webResponse = t.Result; using (System.IO.FileStream fs = new System.IO.FileStream("D:baidu.html", System.IO.FileMode.Create, System.IO.FileAccess.Write)) { using (System.IO.Stream webStream = webResponse.GetResponseStream()) { webStream.CopyTo(fs); } } } catch (WebException ex) { Console.WriteLine("Failed:" + ex.GetBaseException().Message); } finally { if (webResponse != null) webResponse.Close(); } });
3,用代理类型(delegate)实现异步调用。
上面的代码都是在调用.Net类库实现的APM类。难道我们有个方法需要异步执行,也需要去包装,实现BeginXXX和EndXXX以及相关的接口吗?如果是这样,如此复杂的APM谁还会用。幸好,代理Delegate天生就支持APM模型。我们申明了一个delegate后,编译器就会自动为我们生成BeginInvoke和EndInvoke等方法。于是乎,我们要做的工作就简单多了,声明一个和方法签名一致的带类类型。调用它的BgeinInvoke方法和EndInvoke方法。就是这么简单!
//假设:这是我们想要异步执行的方法,它有三个参数,和一个自定义类型的返回值 private UserInfo OurFunction(string param1, string param2, string param3) { //... //... UserInfo uInfo = new UserInfo(); return uInfo; } //第一步:定义一个和它签名一致的代理类型 private delegate UserInfo OurFunctionDelegate(string param1, string param2, string param3);
private void MainFunction() { //第二步:声明这个代理类型
OurFunctionDelegate hander = new OurFunctionDelegate(OurFunction); //第三步:调用BgeinInvoke方法,同时传入一个回调函数 hander.BeginInvoke("p1", "p2", "p3", AsyncCallBack, hander); } //第四步:定义一个回调函数 private void AsyncCallBack(IAsyncResult result) { OurFunctionDelegate hander = (OurFunctionDelegate)result.AsyncState; try { //函数异步执行完成,会进入这个函数,调用EndInvoke方法可以得到结果,如有异常,也会在这里抛出 UserInfo info = hander.EndInvoke(result); } catch (Exception) { //处理异步操作的异常,当然,这里的异常应该是ArrgregateException。 } }
注意事项:对于异步调用获取结果的方式,主要有以下几种。
1,利用AsyncCallBack回调函数,它是不阻塞的。(首选)
2,利用IAsyncResult的Result。调用线程遇到这句话会阻塞。
3,利用IAsyncResult的AsyncWaitHandle.WaitOne()。和2一样会阻塞。
4,轮询IAsyncResult的IsCompleted属性。同样会耗CPU资源,阻塞调用线程,如新开一个线程来轮询又浪费线程资源。