WCF技术剖析之十一:异步操作在WCF中的应用(上篇)

From: http://www.cnblogs.com/artech/archive/2009/07/08/1519423.html


按照操作执行所需的资源类型,我们可以将操作分为CPU绑定型(CPU Bound)操作和I/O绑定型(I/O Bound)操作。对于前者,操作的执行主要利用CPU进行密集的计算,而对于后者,大部分的操作处理时间花在I/O操作处理,比如访问数据库、文件系统、网络资源等。对于I/O绑定型操作,我们可以充分利用多线程的机制,让多个操作在自己的线程并发执行,从而提高系统性能和响应能力。服务调用就是典型的I/O绑定型操作,所以多线程在服务调用中具有广泛的应用。在本篇文章中,我们专门来讨论多线程或者是异步操作在WCF中的具体应用。

如果按照异步操作发生的位置,我个人将WCF应用的异步操作分为下面3种变体。

  • 异步信道调用:客户端通过绑定创建的信道向服务端发送消息,从而实现了对服务的调用,不管消息通过信道向服务端发送的方式是同步的(采用请求-回复MEP进行消息交换)还是异步的(采用单向MEP进行消息交换),客户端程序都可以通过代理对象异步地调用信道,从而实现异步服务调用;
  • 单向(One-way)消息交换:客户端的信道通过单向的消息交换模式向服务端发送消息,消息一旦抵达传输层马上返回,从而达到异步服务调用的效果;
  • 异步服务实现:服务端在具体实现服务操作的时候,采用异步调用的方式。

图1清晰地揭示了以上3种异步场景在整个服务调用中所发生的时机。对于这3种典型的异步操作,它们之间是相互独立的。对于单向消息交换,由于在上面一节中已经进行过详细的介绍,在本节中主要介绍其余两种异步操作的具体使用。本篇文章我们着重探讨第一种形式(异步信道调用)的异步调用,关于异步服务的实现放在下篇中。

clip_image002

图1 WCF多线程应用的三种典型场景

为了方便客户端进行异步的服务调用,最简便的方式就通过SvcUtil.exe这个代码生成工具帮助我们生成机遇异步调用的服务代理类。由于SvcUtil.exe同时也为VS提供了添加服务引用的实现,异步服务代理也可以通过添加服务引用的方式创建。在具体通过服务代理进行异步服务调用的时候,可以采用不同的调用形式,不仅可以采用参数典型的BeginXxx和EndXxx的形式,也可以采用回调(Callback)的形式,还可以采用事件注册的形式。

一、异步服务代理的创建

对于任何一个服务操作,不管它是否采用了异步的实现方式,也不管是否采用单向的消息交换模式,我们均可以通过添加服务引用或者直接使用SvcUtil.exe的方式创建异步服务代理,对服务进行异步调用。

如果通过添加服务引用的方式来创建异步服务代理,只需要在添加服务引用对话框中点击“高级(Advanced)”按钮,便会弹出如下一个“服务引用设置(Service Reference Settings)”对话框,勾选“生成异步操作(Generate asynchronous operations)”复选框即可,如图2所示。

clip_image004

图2 添加服务引用时生成异步操作的设置

通过这种方式生成的代理类与没有选择“生成异步操作”选项一样,都是生成一个继承自ClientBase<TChannel>的类,所不同的是,该类中会多出一些与异步服务调用相关的成员。我们同样以我们的CalculatorService为例(服务契约的定义如下)。

   1: [ServiceContract(Namespace="urn:artech.com")]
   2: public interface ICalculator
   3: {
   4:     [OperationContract]
   5:     double Add(double x, double y);
   6: }

通过这种方式生成的代理类CalculateClient会多出下面列出的事件和方法成员。

   1: public partial class CalculateClient : ClientBase< ICalculator>, ICalculator
   2: {
   3:     //其他成员
   4:     public event System.EventHandler<AddCompleteEventArgs> AddComplete;
   5:     public IAsyncResult BeginAdd(double x, double y, AsyncCallback callback, object asyncState)
   6:     {
   7:         //省略实现
   8:     }
   9:     
  10:     public double EndAdd(System.IAsyncResult result)
  11:     {
  12:         //省略实现
  13:     }
  14:     
  15:     public void AddAsync(double x, double y)
  16:     {
  17:         //省略实现
  18:     }
  19:  
  20:     public void AddAsync(double x, double y, object userState)
  21:     {
  22:         //省略实现
  23:     }
  24: }

事件AddComplete将在Add操作执行之后触发,你可以注册该事件,在运算结束之后做一些特殊的工作,比如运算结果的显示。该事件包含一个特殊的EventArgs:AddCompleteEventArgs。该事件参数类型同样是通过添加服务引用自动创建的。AddCompleteEventArgs继承自System.ComponentModel.AsyncCompleteEventArgs。在事件处理器中可以通过该参数得到异步方法执行的结果(Result属性)和异步操作执行过程中抛出的异常(Error属性),以及得到在执行异步操作显式指定的信息(UserState)。AddCompleteEventArgs和AsyncCompleteEventArgs的定义如下。

   1: public partial class AddCompleteEventArgs : AsyncCompleteEventArgs
   2: {
   3:  
   4:     public AddCompleteEventArgs(object[] results,Exception exception, bool cancelled, object userState) :
   5:         base(exception, cancelled, userState)
   6:     {
   7:         //省略实现
   8:     }
   9:  
  10:     public double Result
  11:     {
  12:         get
  13:         {
  14:            //省略实现
  15:         }
  16:     }
  17: }

 

   1: public class AsyncCompleteEventArgs : EventArgs
   2: {
   3:     public bool Cancelled { get; }
   4:     public Exception Error { get; }
   5:     public object UserState { get; }
   6: }

二、通过BeginXxx/EndXxx进行异步服务调用

接下来我将介绍3种不同的执行异步服务调用的方式,为了简单起见,我们以上面提到的CalculatorService为例演示通过异步操作得到运算结果,并将结果输出。首先采用传统的异步编程模式BeginXxx/EndXxx,如下面的代码所示,在调用BeginAdd方法后,可以做一些额外的处理工作,这些工作将会和Add服务操作的调用并发地运行,最终的运算结果通过EndAdd方法得到。

   1: CalculateClient proxy = new CalculateClient();
   2: IAsyncResult asynResult = proxy.BeginAdd(1, 2, null, null);
   3: //其他操作
   4: double result = proxy.EndAdd(asynResult);
   5: proxy.Close();
   6: Console.WriteLine("x + y = {2} when x = {0} and y = {1}", 1, 2, result);

三、通过回调的方式进行异步服务调用

通过上面的方式进行异步调用有一个不好的地方,就是当EndAdd方法被执行的时候,如果异步执行的方法Add没有执行结束的话,该方法将会阻塞当前线程并等待异步方法的结束,往往不能起到地多线程并发执行应有的作用。我们真正希望的是在异步执行结束后自动回调设定的操作,这样就可以采用回调的方式来实现这样的机制了。

在下面的代码中,我们通过一个匿名方法的形式定义回调操作,由于在回调操用中输出运算结果时需要使用到参与运算的操作数,我们通过BeginAdd方法的最后一个object类型参数实现向回调操作传递数据,在回调操作中通过IAsyncResult对象的AsyncState获得。

   1: CalculateClient proxy = new CalculateClient();
   2: proxy.BeginAdd(1, 2,
   3:     delegate(IAsyncResult asyncResult)
   4:     {
   5:         double[] operands = asyncResult.AsyncState as double[];
   6:         double result = proxy.EndAdd(asyncResult);
   7:         proxy.Close();
   8:         Console.WriteLine("x + y = {2} when x = {0} and y = {1}", operands[0], operands[1], result);
   9:     }, new double[]{1,2});

四、通过事件注册的方式进行异步服务调用

实际上,事件注册和通过回调从表现上看比较类似,当操作结束之后,对于前者通过触发事件的方式执行相应的操作,而对于后者直接执行指定的回调操作。如果采用事件注册的方式,上面的代码就可以改写成下面的形式。通过AddAsync开始异步操作,如果需要向AddComplete事件传递数据,可以使用该方法的第3个参数userState(该参数和BeginAdd的第4个参数asyncState具有相似的作用),设定的值可以通过AddCompleteEventArgs的UserState属性获得,而操作执行的结果则通过AddCompleteEventArgs的Result属性获得。

   1: CalculateClient proxy = new CalculateClient();
   2: proxy.AddComplete += delegate(object sender, AddCompleteEventArgs args)
   3: {
   4:     double[] operands = args.UserState as double[];
   5:     double result = args.Result;
   6:     proxy.Close();
   7:     Console.WriteLine("x + y = {2} when x = {0} and y = {1}", operands[0], operands[1], result);
   8: };
   9: proxy.AddAsync(1, 2,new double[]{1,2}); 
作者: Artech

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/402973.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

vue2使用vant组件库;使用rem后vant组件样式变小了。

vue2使用vant组件库 文章目录vue2使用vant组件库一、vant是什么&#xff1f;二、使用步骤1.引入vant2库2.引入 自动按需引入组件3.在main.js中按需引入组件&#xff08;推荐&#xff09;4.或者只是在某个index.vue内使用&#xff08;推荐&#xff09;5.在main.js中导入所以组件…

EditPlus自定义模板

直接用图表达了,不详之处可以留言. 1.查看帮助中的关于,确定文本编辑器的版本是否一致 2.如图 3.如图 4.这个test.html 需要事先编辑并拷贝到EditPlus的安装目录 5.新建空白html 时,效果如下: 6.方便大家,代码贴上来. html> <head> <title>网页标题…

【Android】Android开发启动app弹出一张广告图片,Dialog可以查看大图,查看某个图片功能...

作者&#xff1a;程序员小冰&#xff0c;GitHub主页&#xff1a;https://github.com/QQ986945193 新浪微博&#xff1a;http://weibo.com/mcxiaobing 首先给大家看一下我们今天这个最终实现的效果图&#xff1a; 首先说一下&#xff0c;这里利用的是一个dialog&#xff0c;然…

el-dialog弹框中img图片保持比例最大化;图片保持比例最大化

图片保持比例最大化 <el-dialog :visible.sync"dialogVisible" center class"look_img_dia"><img width"100%" :src"dialogImageUrl" alt"" /></el-dialog>.look_img_dia {/deep/.el-dialog {margin-top…

Wireshark技巧-过滤规则和显示规则

From: http://www.cnblogs.com/icez/p/3973873.html Wireshark是一个强大的网络协议分析软件&#xff0c;最重要的它是免费软件。 过滤规则 只抓取符合条件的包&#xff0c;在Wireshark通过winpacp抓包时可以过滤掉不符合条件的包&#xff0c;提高我们的分析效率。 如果要填写过…

easyUI 展开DataGrid里面的行显示详细信息

http://blog.csdn.net/yanghongchang_/article/details/7854156原著 datagrid 可以改变它的view(视图)去显示不同的效果.使用详细视图,datagrid可以显示展开按钮("" 或者 "-")在数据行的左边,用户可以展开一个行去显示一个附加的详细信息. 查看 Demo 步骤 …

理解OAuth 2.0(转)

From: http://www.mamicode.com/info-detail-1610036.html 理解OAuth 2.0 作者&#xff1a; 阮一峰 日期&#xff1a; 2014年5月12日 OAuth是一个关于授权&#xff08;authorization&#xff09;的开放网络标准&#xff0c;在全世界得到广泛应用&#xff0c;目前的版本是2.0版。…

wcf返回datatable必须给tablename赋值

From: http://www.cnblogs.com/hxw/archive/2010/07/10/1774841.html 最近在学习WCF,返回datatable的时候老是出现“An error occurred while receiving the HTTP response to http://localhost:9999/calculatorservice. This could be due to the service endpoint binding no…

微信支付开发(1) JS API支付

From: http://www.cnblogs.com/txw1958/p/wxpayv3-jsapi.html 关键字&#xff1a;微信支付 微信支付v3 jsapi支付 统一支付 Native支付 prepay_id 作者&#xff1a;方倍工作室 原文: http://www.cnblogs.com/txw1958/p/wxpayv3-jsapi.html 本文介绍微信支付下的jsapi实现流程…

el-upload多文件上传;el-upload采用递归依次上传文件;el-upload采用递归在上一个文件上传成功后再传下一个文件

场景&#xff1a; 需求是接口一次上传一个文件&#xff0c;前一个文件上传成功后再调下一个接口上传下一个文件。 el-upload本身就支持多文件上传。但是它是并发进行&#xff0c;例如&#xff1a;选择一千个文件后&#xff0c;是一千个文件自动立马并行调用一千个后端接口去上传…

Controller向View传值方式总结

From: http://www.cnblogs.com/guohu/p/4377974.html 总结发现ASP.NET MVC中Controller向View传值的方式共有6种&#xff0c;分别是&#xff1a; ViewBagViewDataTempData向普通View页面传一个Model对象向强类型页面传传一个Model对象用一个ViewModel对象解决所有问题 首先我们…

记录一次bug解决过程:数据迁移

一 总结不擅长语言表达&#xff0c;勤于沟通&#xff0c;多锻炼 调试MyBatis中SQL语法:foreach 问题&#xff1b;缺少关键字VALUES。很遗憾&#xff1a;它的错误报的让人找不着北。 二 BUG描述&#xff1a;MyBatis中批量插入数据异常 <?xml version"1.0" encodin…

繁华模拟赛 ljw分雕塑

/* 用f[i][k]表示考虑到第i个雕塑&#xff0c;分成k组&#xff0c;可不可行&#xff08;这是一个bool类型的数组&#xff09; 转移&#xff1a; f[i][k]f[j][k-1],sum[i]-sum[j]合法 */ #include <cstdio> #include <cstdlib> #include <cstring> #include &…

Razor语法大全

From: http://www.cnblogs.com/dengxinglin/p/3352078.html Razor是基于framewor4以上写的一个开源项目&#xff1a;https://github.com/Antaris/RazorEngine/ Razor是包含了模板引擎和动态编译两部分。本部分就简单记录了模板引擎的一些语法&#xff0c;之后用Razor做一个代码…

el-dialog的内容不刷新;el-dialog内容有缓存;el-dialog里面的组件不刷新问题;

el-dialog里面的内容是带缓存的&#xff0c;也就是说除了第一次打开会初始化&#xff0c;其他次打开都是直接加载缓存的&#xff1b; 这就导致了有时候打开弹框时候&#xff0c;内容不刷新。有说法说是el-dialog嵌套太深大致的。 解决方法&#xff1a;直接给弹框的内容部分添加…

el-badge标记;el-tabs配合el-badge提示数字

标签选项卡配个标记数字提示 注意&#xff1a;el-tabs可以通过具名 slot 来实现选项卡的内容 <template><div><el-tabs v-model"tabValue"><el-tab-pane label"全部" name"1"></el-tab-pane><el-tab-pane lab…

[DP之计数DP]

其实说实在 我在写这篇博客的时候 才刚刚草了一道这样类型的题 之前几乎没有接触过 接触过也是平时比赛的 没有系统的做过 可以说0基础 我所理解的计数dp就是想办法去达到它要的目的 而且一定要非常劲非常快 都是一个很小的数然后有很多种接下来的方案使得这个数一下子变很大 计…

C++程序设计(第2版)课后习题答案--第11章

11.9 定义分数类Rational...... View Code 1 #include<iostream.h>2 #include<stdlib.h>3 class Rational{4 private:5 int fm,fz;6 int getZdgys(int a,int b);7 public:8 Rational(){9 fm1;fz0; 10 } 11 Rational(int a,int b); 1…

提交本地项目到github

要托管到github&#xff0c;那你就应该要有一个属于你自己的github帐号&#xff0c;所以你应该先到github.com注册 打开浏览器 在地址栏输入地址&#xff1a;github.com 填写用户名、邮箱、密码 点击Sign up即可简单地注册 2完成注册&#xff0c;进入github平台&#xff0c; 点…

php 的命名空间 看鸟哥后的随笔

我以前貌似真心没有想过php的命名空间&#xff0c;我每次写文件都会记得不让类名相重&#xff0c; 看完命名空间了这个&#xff0c;我发现可以解决我的一部分问题 1 MyLove.php2 namespace Zj;3 class Application{4 public function toMyLove(){5 echo Marx is…