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

按照操作执行所需的资源类型,我们可以将操作分为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种典型的异步操作,它们之间是相互独立的。对于单向消息交换,由于在上面一节中已经进行过详细的介绍,在本节中主要介绍其余两种异步操作的具体使用。本篇文章我们着重探讨第一种形式(异步信道调用)的异步调用,关于异步服务的实现放在下篇中。

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

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

一、异步服务代理的创建

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

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

图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://artech.cnblogs.com
本文版权归作者和博客园共有,欢迎转载,但未经作者同意必须保留此段声明,且在文章页面明显位置给出原文连接,否则保留追究法律责任的权利。

WCF技术剖析之十一:异步操作在WCF中的应用(上篇)的更多相关文章

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

    原文:WCF技术剖析之十一:异步操作在WCF中的应用(下篇) 说完了客户端的异步服务调用(参阅WCF技术剖析之十一:异步操作在WCF中的应用(上篇)),我们在来谈谈服务端如何通过异步的方式为服务提供实 ...

  2. WCF技术剖析之七:如何实现WCF与EnterLib PIAB、Unity之间的集成

    原文:WCF技术剖析之七:如何实现WCF与EnterLib PIAB.Unity之间的集成 在这之前,我写过深入介绍MS EnterLib PIAB的文章(参阅<MS Enterprise Li ...

  3. WCF技术剖析之十三:序列化过程中的已知类型(Known Type)

    原文:WCF技术剖析之十三:序列化过程中的已知类型(Known Type) [爱心链接:拯救一个25岁身患急性白血病的女孩[内有苏州电视台经济频道<天天山海经>为此录制的节目视频(苏州话) ...

  4. WCF技术剖析之十:调用WCF服务的客户端应该如何进行异常处理

    原文:WCF技术剖析之十:调用WCF服务的客户端应该如何进行异常处理 在前面一片文章(服务代理不能得到及时关闭会有什么后果?)中,我们谈到及时关闭服务代理(Service Proxy)在一个高并发环境 ...

  5. WCF技术剖析之二十一:WCF基本异常处理模式[中篇]

    原文:WCF技术剖析之二十一:WCF基本异常处理模式[中篇] 通过WCF基本的异常处理模式[上篇], 我们知道了:在默认的情况下,服务端在执行某个服务操作时抛出的异常(在这里指非FaultExcept ...

  6. WCF技术剖析之十五:数据契约代理(DataContractSurrogate)在序列化中的作用

    原文:WCF技术剖析之十五:数据契约代理(DataContractSurrogate)在序列化中的作用 [爱心链接:拯救一个25岁身患急性白血病的女孩[内有苏州电视台经济频道<天天山海经> ...

  7. WCF技术剖析之十四:泛型数据契约和集合数据契约(下篇)

    原文:WCF技术剖析之十四:泛型数据契约和集合数据契约(下篇) [爱心链接:拯救一个25岁身患急性白血病的女孩[内有苏州电视台经济频道<天天山海经>为此录制的节目视频(苏州话)]]在.NE ...

  8. WCF技术剖析之八:ClientBase<T>中对ChannelFactory<T>的缓存机制

    原文:WCF技术剖析之八:ClientBase<T>中对ChannelFactory<T>的缓存机制 和传统的分布式远程调用一样,WCF的服务调用借助于服务代理(Service ...

  9. 《WCF技术剖析》博文系列汇总[持续更新中]

    原文:<WCF技术剖析>博文系列汇总[持续更新中] 近半年以来,一直忙于我的第一本WCF专著<WCF技术剖析(卷1)>的写作,一直无暇管理自己的Blog.在<WCF技术剖 ...

随机推荐

  1. 华为GVRP理解

    类似于CISCO的VTP 在大型的网络中,华为交换机之间的串联是很普遍的.一般交换机互联端口都是配置成Trunk,即允许透传多个VLAN的.对于用户来说,手工配置太麻烦.一个规模比较大的网络可能包含多 ...

  2. 怎样在VC里面使用graphics.h绘图

    网上很多绘图程序和小游戏程序都是用的 TC,在 VC 下编译时提示错误:fatal error C1083: Cannot open include file: 'graphics.h': No su ...

  3. genymotion下载出现Unable to create virtual device,Server returned HTTP status code 0.

    解决方法:

  4. @RequestParam

    @MVC另外一个特性是其提取和解析请求参数的能力.让我们继续重构上面的方法,并在其中添加@RequestParam注解: @RequestMapping("/accounts/show&qu ...

  5. poj 3281 Dining 网络流-最大流-建图的题

    题意很简单:JOHN是一个农场主养了一些奶牛,神奇的是这些个奶牛有不同的品味,只喜欢吃某些食物,喝某些饮料,傻傻的John做了很多食物和饮料,但她不知道可以最多喂饱多少牛,(喂饱当然是有吃有喝才会饱) ...

  6. ST官方翻译的中文应用笔记汇总

    ST官方翻译的中文应用笔记汇总 http://www.51hei.com/stm32/3382.html 官方中文AN:AN3116:STM32? 的 ADC 模式及其应用AN1015:用于提高微控制 ...

  7. Dijkstra、Dij + heap、Floyd、SPFA、 SPFA + SLF Template

    Dijkstra in Adjacency matrix : int Dijkstra(int src,int tec, int n){ ]; ]; memset(done,,sizeof(done) ...

  8. 基于FPGA的DW8051移植(二)

    基于上一篇博文继续,本来想换到oc8051,但是还是不甘心,弄了这么久还是没有弄出来,真是打击屎了. 上一篇说3f进入了operation code所以判断是代码错误,后来发现不可以这么判断. 因为地 ...

  9. 用C语言打印出三角函数

    在网上看到一个实例,是用C 中的* 打印出三角函数cos #include<stdio.h> #include<math.h> int main() { double y; i ...

  10. MediaPlayer视频播放器

    android视频播放 根据apidemo重写.代码如下: package com.jamdeo.tv.livetv.player; import android.media.AudioManager ...