从零开始学WCF(9)会话、实例化和并发

来源:互联网 发布:商家订单导出软件 编辑:程序博客网 时间:2024/05/18 03:35

会话

“会话”是在两个终结点之间发送的所有消息的一种相互关系。这个与ASP.NET的Session是不一样的。会话是标记在WCF服务协定接口上的
在服务协定上设置 System.ServiceModel.ServiceContractAttribute.SessionMode值:
—— Allowed:允许会话( 默认的设置),客户端可以进行连接,并选择建立会话或不建立会话。
—— Required: 所有调用(即支持调用的基础消息交换)都必须是同一个对话的一部分。要求客户端必须在一个会话里调用所有的操作。
—— NotAllowed: 禁止会话。不允许客户端在会话里调用操作

WCF会话具有下列主要概念性功能:
1) 它们由调用应用程序显示启动和终止。
2) 会话期间传递的消息按照接收消息的顺序进行处理。依次来组成相互关联的消息,按顺序来处理。
3) 会话将一组消息相互关联,从而形成对话。该关联的含义是抽象的。例如,一个基于会话的通道可能会根据共享网络连接来关联消息,而另一个基于会话的通道可能会根据消息正文中的共享标记来关联消息。可以从会话派生的功能取决于关联的性质。
4) 不存在于WCF会话相关联的常规数据存储区。会话只是一系列的服务协定方法的调用,从而形成一个会话。

ASP.NET会话和WCF会话的区别
1) ASP.NET会话总是由服务器启动。
2) ASP.NET会话原本是无序的。
3) ASP.NET会话提供了一种跨请求的常规数据存储机制。

客户端应用程序和服务应用程序以不同的方式与会话交互。
——客户端应用程序启动会话,然后接收并处理在该会话内发送的消息。
——服务应用程序可以将会话用作扩展点,以添加其他行为。通过直接使用InstanceContext或实现一个自定义实例上下文提供程序,可以做到这一点。

当WCF服务接受客户端会话时,默认情况下启用一下功能:
1) 通过同一用户定义的服务对象来处理WCF客户端对象之间的所有调用。服务对象是使用同一个对象来处理所有操作的调用。
2) 除了此实例行为之外,不同的基于会话的绑定还会提供其他功能。

系统提供的会话类型:
1) System.ServiceModel.Channels.SecurityBindingElement 支持基于安全的会话,其中,两个通信端采用统一的安全对话。
2) System.ServiceModel.WSHttpBinding 绑定(包含对安全会话和可靠会话的支持)默认情况下只使用对消息进行加密和数字签名的安全会话。
3) System.ServiceModel.NetTcpBinding 绑定支持基于TCP/IP的会话,以确保所有消息都由套接字级别的连接进行关联。
4) System.ServiceModel.Channels.ReliableSessionBindingElement 元素实现WS-ReliableMessaging 规范,并提供对可靠会话的支持。在可靠会话中,可以配置消息以按顺序传递并且只传递一次,从而使消息在对话期间即使经过多个节点也可以确保保密性。
5) System.ServiceModel.NetMsmqBinding 绑定提供MSMQ数据报会话。

DEMO

1) 还是跟以前一样新建完WCF Service Application后,定义WCF服务接口:
using System;using System.Collections.Generic;using System.Linq;using System.Runtime.Serialization;using System.ServiceModel;using System.Text;namespace Video9.Demo1.Session{    // 设置SessionMode为Required,这就说明该服务必须在Session会话里调用所有的协定方法。    [ServiceContract(Namespace = "http://Video9.Demo1.Session",SessionMode=SessionMode.Required)]    public interface ICalculatorService    {        [OperationContract(IsOneWay = true)]        void Clear();        [OperationContract(IsOneWay = true)]        void AddTo(double n);        [OperationContract(IsOneWay = true)]        void SubstractFrom(double n);        [OperationContract(IsOneWay = true)]        void MultiplyBy(double n);        [OperationContract(IsOneWay = true)]        void DivideBy(double n);        [OperationContract]        double Result();    }}

2) 实现该服务接口:
using System;using System.Collections.Generic;using System.Linq;using System.Runtime.Serialization;using System.ServiceModel;using System.Text;namespace Video9.Demo1.Session{    // 指定了一个服务的行为ServiceBehavior,InstanceContextMode实例化的行为指定为单个实例Single,也就是说    //所有这些针对服务方法的调用在服务器上都是使用单个实例来提供这些方法的处理。    [ServiceBehavior(InstanceContextMode=InstanceContextMode.Single)]    public class CalculatorService : ICalculatorService    {        double result = 0.0D;        public void Clear()        {            result = 0.0D;        }        public void AddTo(double n)        {            result += n;        }        public void SubstractFrom(double n)        {            result -= n;        }        public void MultiplyBy(double n)        {            result *= n;        }        public void DivideBy(double n)        {            result /= n;        }        public double Result()        {            return result;        }    }}

3) 定义Web.Config配置文件,这里需要注意的是需要定义binding类型为我们上面所提到的系统提供的会话类型中的类型,我们这里定义为wsHttpBinding。
<?xml version="1.0"?><configuration>  <system.web>    <compilation debug="true" targetFramework="4.0" />  </system.web>  <system.serviceModel>        <services>      <service name="Video9.Demo1.Session.CalculatorService" behaviorConfiguration="CalculatorServiceBehavior">        <endpoint address="" binding="wsHttpBinding" contract="Video9.Demo1.Session.ICalculatorService" bindingConfiguration="BindingWithSession"/>        <endpoint address="mex" binding ="mexHttpBinding" contract="IMetadataExchange"/>      </service>    </services>    <bindings>      <wsHttpBinding>        <binding name="BindingWithSession">          <!--打开当前绑定上的会话支持,如果不打开这个会话的支持的话就无法调用Session操作-->          <reliableSession enabled="true"/>        </binding>      </wsHttpBinding>    </bindings>        <behaviors>      <serviceBehaviors>        <behavior name="CalculatorServiceBehavior">          <!-- To avoid disclosing metadata information, set the value below to false and remove the metadata endpoint above before deployment -->          <serviceMetadata httpGetEnabled="true"/>          <!-- To receive exception details in faults for debugging purposes, set the value below to true.  Set to false before deployment to avoid disclosing exception information -->          <serviceDebug includeExceptionDetailInFaults="false"/>        </behavior>      </serviceBehaviors>    </behaviors>    <serviceHostingEnvironment multipleSiteBindingsEnabled="true" />  </system.serviceModel> <system.webServer>    <modules runAllManagedModulesForAllRequests="true"/>  </system.webServer>  </configuration>

4) 发布部署到IIS上,然后添加客户端程序Client,这里所获取的值并不是存在与服务端的某些对象里,而是由WCF的基础结构来承载这些数据。
using System;using System.Collections.Generic;using System.Linq;using System.Text;namespace Client{    class Program    {        static void Main(string[] args)        {            ServiceReference1.CalculatorServiceClient client = new ServiceReference1.CalculatorServiceClient();            //一下都是在一个实例、一个会话里运行的,虽然赋值并没有取得值,但是会把值保存在会话中。            //这一系列的方法都是在一个会话中调用。在调用第一个Clear的时候就已经启动了会话。            client.Clear();            client.AddTo(10.00D);            client.SubstractFrom(5.00D);            client.MultiplyBy(2.00);            client.DivideBy(5.00);            double result = client.Result();            Console.WriteLine(result);            client.Close();            Console.ReadKey();        }    }}

5) 我们打开客户端的app.config可以看到:
                    <reliableSession ordered="true" inactivityTimeout="00:10:00"                        enabled="true" />

如果我们这里把可靠性的会话reliableSession的enable属性改为false的话,也就是禁止了Session会话的支持,这会导致出现异常报错。

源代码:http://download.csdn.net/detail/eric_k1m/6405677

实例化

实例化行为(使用System.ServiceModel.ServiceBehaviorAttribute.InstanceContextMode属性进行设置)控制如何创建InstanceContext以响应传入的消息。也就是指定以怎样的方式来来实例化我们的服务类。实例化是标记在WCF服务的实现类上的。
默认情况下,每个InstanceContext都与一个用户定义服务对象相关联,因此(在默认情况下)设置InstanceContextMode属性也可以控制用户定义服务对象的实例化。

可以使用下列实例化模型:
—— PerCall: 为每个客户端请求创建一个新的InstanceContext(以及相应的服务对象)。每一次调用都创建一个新的InstanceContext以及服务实例。
—— PerSession: 为每个新的客户端会话创建一个新的InstanceContext(以及相应的服务对象),并在该会话的生存期内对其进行维护(这需要使用支持会话的绑定)。
—— Single: 单个InstanceContext(以及相应的服务对象)处理应用程序生存期内的所有客户端请求。所有的客户端请求都是由服务端一个对象来处理,这就需要排队可能会阻塞。

DEMO

1) 新建WCF Service Application,添加服务接口
using System;using System.Collections.Generic;using System.Linq;using System.Runtime.Serialization;using System.ServiceModel;using System.Text;namespace Video9.Demo2.Instancing{    // 创建WCF服务接口,指定该服务的SessionMode是Required,也就是要求客户端必须在一个会话里调用所有服务操作。    [ServiceContract(Namespace = "http://Video9.Demo2.Instancing",SessionMode=SessionMode.Required)]    public interface ICalculatorService    {        [OperationContract]        double Add(double n1, double n2);        [OperationContract]        double Substract(double n1, double n2);        [OperationContract]        double Multiply(double n1, double n2);        [OperationContract]        double Divide(double n1, double n2);    }}

2) 定义一个接口接口来继承ICalculatorService,用于监视instance实例的状态。
using System;using System.Collections.Generic;using System.Linq;using System.Text;using System.ServiceModel;namespace Video9.Demo2.Instancing{    //定义一个服务协定来监视instance实例的状态    //定义SessionMode为Required,也就是客户端所有的请求都在这一个Session里调用所有服务端操作。    //当用ICalculatorServiceInstance接口来继承ICalculatorService接口的时候,实现继承接口ICalculatorServiceInstance的话,    //会实现ICalculatorServiceInstance和ICalculatorService两个接口中的所有方法。    [ServiceContract(Namespace = "http://Video9.Demo2.Instancing",SessionMode=SessionMode.Required)]    public interface ICalculatorServiceInstance:ICalculatorService    {        [OperationContract]        string GetInstanceContextMode();        [OperationContract]        int GetInstanceId();        [OperationContract]        int GetOperationCount();    }}

3) 实现该服务:
using System;using System.Collections.Generic;using System.Linq;using System.Runtime.Serialization;using System.ServiceModel;using System.Text;using System.Threading;namespace Video9.Demo2.Instancing{    //PerSession是为每个新的客户端会话创建一个新的Instance,以及相应的服务对象。    //这种请求是需要一个支持Session会话的绑定binding    [ServiceBehavior(InstanceContextMode=InstanceContextMode.PerSession)]    //当用ICalculatorServiceInstance接口来继承ICalculatorService接口的时候,实现继承接口ICalculatorServiceInstance的话,    //会实现ICalculatorServiceInstance和ICalculatorService两个接口中的所有方法。    public class CalculatorService : ICalculatorServiceInstance    {        static Object syncObject = new object();        static int instanceCount;        int instanceId;        int operationCount;        public CalculatorService()        {            //lock关键字将语句块标记为临界区,方法是获取给定对象的互斥锁,执行语句,然后释放该锁。            //lock关键字可确保当一个线程位于代码的临界区时,另一个线程不会进入该临界区。如果其他线程视图进入锁定的代码,            //则他将一直等待(即被阻止),直到该对象被释放。            lock (syncObject)            {                instanceCount++;                instanceId = instanceCount;            }        }        public string GetInstanceContextMode()        {            //返回服务端的InstanceContextMode            //OperationContext.Current.Host——获取当前OperationContext的HOST服务的主机。            //ServiceHost类是提供服务的主机,也就是实现WCF服务编程模型所使用的主机。            ServiceHost host = (ServiceHost)OperationContext.Current.Host;            //获取CalculatorService这个实现接口类所设定的ServiceBehavior的值。            ServiceBehaviorAttribute behavior = host.Description.Behaviors.Find<ServiceBehaviorAttribute>();            //从ServiceBehavior中获取InstanceContextMode属性的值            return behavior.InstanceContextMode.ToString();        }        public int GetInstanceId()        {            //返回instance实例的ID            return instanceId;        }        public int GetOperationCount()        {            //返回ICalculatorService的Operation执行实例的operationCount            lock (syncObject)            {                return operationCount;            }        }        public double Add(double n1, double n2)        {            operationCount++;            return n1 + n2;        }        public double Substract(double n1, double n2)        {            //Interlocked.Increment(ref Int32)方法:以原子操作的形式递增指定变量的值并存储结果。            //也就等同于operationCount++            Interlocked.Increment(ref operationCount);            return n1 - n2;        }        public double Multiply(double n1, double n2)        {            Interlocked.Increment(ref operationCount);            return n1 * n2;        }        public double Divide(double n1, double n2)        {            Interlocked.Increment(ref operationCount);            return n1 / n2;        }    }}

4) 配置配置文件Web.config文件,注意这里的contract是ICalculatorServiceInstance,而且必须要reliableSession enabled=true才可以支持Session。

<?xml version="1.0"?><configuration>  <system.web>    <compilation debug="true" targetFramework="4.0" />  </system.web>  <system.serviceModel>    <services>      <service name="Video9.Demo2.Instancing.CalculatorService" behaviorConfiguration="CalculatorServiceBehavior">        <endpoint address="" binding="wsHttpBinding" bindingConfiguration="BindingWithSession" contract="Video9.Demo2.Instancing.ICalculatorServiceInstance"/>        <endpoint address="mex" binding="mexHttpBinding" contract="IMetadataExchange"/>      </service>    </services>    <bindings>      <wsHttpBinding>        <binding name="BindingWithSession">          <!--这里一定要设定为true,才支持使用Session-->          <reliableSession enabled="true"/>        </binding>      </wsHttpBinding>    </bindings>        <behaviors>      <serviceBehaviors>        <behavior name="CalculatorServiceBehavior">          <!-- To avoid disclosing metadata information, set the value below to false and remove the metadata endpoint above before deployment -->          <serviceMetadata httpGetEnabled="true"/>          <!-- To receive exception details in faults for debugging purposes, set the value below to true.  Set to false before deployment to avoid disclosing exception information -->          <serviceDebug includeExceptionDetailInFaults="false"/>        </behavior>      </serviceBehaviors>    </behaviors>    <serviceHostingEnvironment multipleSiteBindingsEnabled="true" />  </system.serviceModel> <system.webServer>    <modules runAllManagedModulesForAllRequests="true"/>  </system.webServer>  </configuration>

5) 部署发布到IIS上后,添加客户端项目Client,添加WCF服务引用,进行测试:
using System;using System.Collections.Generic;using System.Linq;using System.Text;namespace Client{    class Program    {        static void Main(string[] args)        {            ServiceReference1.CalculatorServiceInstanceClient client = new ServiceReference1.CalculatorServiceInstanceClient();            string instanceMode = client.GetInstanceContextMode(); //获得服务端的InstanceContextMode            Console.WriteLine("InstanceContextMode是: {0}", instanceMode);            DoCalculations(client);            //在生成一个客户端代理类的实例,用来调用服务端的方法            ServiceReference1.CalculatorServiceInstanceClient client2 = new ServiceReference1.CalculatorServiceInstanceClient();            DoCalculations(client2);            client.Close();            client2.Close();            Console.ReadKey();        }        private static void DoCalculations(ServiceReference1.CalculatorServiceInstanceClient client)        {            // Call the Add service operation.            double value1 = 100.00D;            double value2 = 15.99D;            double result = client.Add(value1, value2);            Console.WriteLine("Add({0},{1}) = {2}", value1, value2, result);            Console.Write("InstanceId: {0}", client.GetInstanceId());            Console.WriteLine(" , OperationCount: {0}", client.GetOperationCount());            // Call the Subtract service operation.            value1 = 145.00D;            value2 = 76.54D;            result = client.Substract(value1, value2);            Console.WriteLine("Subtract({0},{1}) = {2}", value1, value2, result);            Console.Write("InstanceId: {0}", client.GetInstanceId());            Console.WriteLine(" , OperationCount: {0}", client.GetOperationCount());            // Call the Multiply service operation.            value1 = 9.00D;            value2 = 81.25D;            result = client.Multiply(value1, value2);            Console.WriteLine("Multiply({0},{1}) = {2}", value1, value2, result);            Console.Write("InstanceId: {0}", client.GetInstanceId());            Console.WriteLine(" , OperationCount: {0}", client.GetOperationCount());            // Call the Divide service operation.            value1 = 22.00D;            value2 = 7.00D;            result = client.Divide(value1, value2);            Console.WriteLine("Divide({0},{1}) = {2}", value1, value2, result);            Console.Write("InstanceId: {0}", client.GetInstanceId());            Console.WriteLine(" , OperationCount: {0}", client.GetOperationCount());            Console.WriteLine("——————————————————————");        }    }}

6) 第一次运行测试结果:可以看出来当使用了PerSession的实例化模型后,当在同一个Session里的时候实例模型只有一个。只有在重新生成客户端代理类在调用的时候,才会生成新的Session,也就会使得服务端生成一个新的实例模型。


7) 点击停止客户端程序后,在此运行的话可以看到InstanceID变成了3,4。这是由于上面的测试已经导致InstanceID变成了2,当在此运行客户端程序的时候,就会从2开始加1计算。



8) 修改WCF服务的实现类的ServiceBehavior的InstanceContextMode=InstanceContextMode.PerCall。
    //PerCall是为每一个客户端的请求服务端的实现类都创建一个新的实例来处理请求的方法。    [ServiceBehavior(InstanceContextMode=InstanceContextMode.PerCall)]

9) 然后再重新编译部署到IIS上,运行客户端程序可以看到,每次调用WCF服务端的方法的时候,InstanceID都会+1。这是由于instanceCount是Static静态类型的参数,没生成一个新的实例,并不会使得Static类型的参数重新生成,所以当调用InstanceContextMode的时候是1,Add的时候是2,所以这时候在调用InstanceID的时候就成为了3。那么OperationCount为什么一直都是0呢?这是由于每次调用WCF服务的时候,服务端都是启用一个新的实例,但是operationCount这个变量并不是一个static的变量,所以每生成一个实例都会生成一个全新的operationCount变量。



10) 这个修改WCF实现类的ServiceBehavior为:
    //Single指的是不管有多少客户端来调用该服务类里的方法,服务类只会提供一个实例来处理这些请求。    [ServiceBehavior(InstanceContextMode=InstanceContextMode.Single)]

11) 运行结果如下:InstanceID永远都是1,这是由于instanceCount是Static类型的,而且设置了ServiceBehavior为Single,所以就导致不管有多少的客户端请求,服务端都只有一个实例来出来; OpeCount是一只在增长的,这是由于operationCount并不是Static的,所以在相同的一个实例里没调用一次方法他都会增加1。


源代码: http://download.csdn.net/detail/eric_k1m/6407783


并发

并发是对InstanceContext中在任一时刻处于活动状态的线程数量的控制。其实就是多线程的意思。
此控制是通过将 System.ServiceModel.ServiceBehaviorAttribute.ConcurrencyMode与 ConcurrencyMode枚举结合使用来实现。

有以下三种可用的并发模式:
1) Single: 最多允许每个实例上下文同时拥有一个对该实例上下文的消息进行处理的线程。其他希望使用同一实例上下文的线程必须一致阻塞,知道原始线程退出该实例上下文为止。也就是每个实例单线程。
2) Multiple: 每个服务实例都可以拥有多个同时处理消息的线程。若要使用此并发模式,服务实现必须是线程安全的。也就是每个实例多线程。
3) Reentrant: 每个服务实例一次只能处理一个消息,但可以接受可重入的操作调用。仅当服务通过WCF客户端对象提供服务时才会接受这些调用。也就是说每个实例消息一次只可以处理一个消息。

并发的使用与实例化模式有关。在PerCall实例化过程中,与并发没有关系,因为每个消息都有一个新的InstanceContext处理,因此InstanceContext中处于活动状态的线程永远都不会超过一个。

会话与InstanceContext设置进行交互

SessionMode属性值和InstanceContextMode属性值组合的情况下,支持会话或不支持会话的传入通道的结果:


DEMO

1) 与前面的DEMO类似,也是生成一个WCF服务CalculatorService,然后再WCF服务接口ICalculatorService中定义WCF服务接口:
using System;using System.Collections.Generic;using System.Linq;using System.Runtime.Serialization;using System.ServiceModel;using System.Text;namespace Video9.Demo3.Concurrency{    //定义WCF服务接口,注意这里并没有指定Session会话    [ServiceContract(Namespace = "http://Video9.Demo3.Concurrency")]    public interface ICalculatorService    {        [OperationContract]        double Add(double n1, double n2);        [OperationContract]        double Subtract(double n1, double n2);        [OperationContract]        double Multiply(double n1, double n2);        [OperationContract]        double Divide(double n1, double n2);    }}

2) 定义一个新的WCF服务接口ICalculatorServiceConcurrency,该接口继承自ICalculatorService,并添加了两个方法:
using System;using System.Collections.Generic;using System.Linq;using System.Text;using System.ServiceModel;namespace Video9.Demo3.Concurrency{    [ServiceContract(Namespace = "http://Video9.Demo3.Concurrency")]    public interface ICalculatorServiceConcurrency:ICalculatorService    {        [OperationContract]        string GetConcurrencyMode();        [OperationContract]        int GetOperationCount();    }}

3) 实现该WCF服务接口ICalculatorServiceConcurrency:
using System;using System.Collections.Generic;using System.Linq;using System.Runtime.Serialization;using System.ServiceModel;using System.Text;namespace Video9.Demo3.Concurrency{    // 设置实现类的线程为单线程,实例模型为Single    [ServiceBehavior(ConcurrencyMode=ConcurrencyMode.Single,InstanceContextMode=InstanceContextMode.Single)]    public class CalculatorService : ICalculatorServiceConcurrency    {        int operationCount;        public string GetConcurrencyMode()        {            //返回服务的并发模式            ServiceHost host = (ServiceHost)OperationContext.Current.Host;            ServiceBehaviorAttribute behavior = host.Description.Behaviors.Find<ServiceBehaviorAttribute>();            return behavior.ConcurrencyMode.ToString();        }        public int GetOperationCount()        {            System.Threading.Thread.Sleep(5000);            return operationCount;        }        public double Add(double n1, double n2)        {            operationCount++;            System.Threading.Thread.Sleep(1000);            return n1 + n2;        }        public double Subtract(double n1, double n2)        {            operationCount++;            System.Threading.Thread.Sleep(2000);            return n1 - n2;        }        public double Multiply(double n1, double n2)        {            operationCount++;            System.Threading.Thread.Sleep(3000);            return n1 * n2;        }        public double Divide(double n1, double n2)        {            operationCount++;            System.Threading.Thread.Sleep(4000);            return n1 / n2;        }    }}


4) 配置配置文件Web.config:
<?xml version="1.0"?><configuration>  <system.web>    <compilation debug="true" targetFramework="4.0" />  </system.web>  <system.serviceModel>    <services>      <service name="Video9.Demo3.Concurrency.CalculatorService" behaviorConfiguration="CalculatorServiceBehavior">        <endpoint address="" binding="wsHttpBinding" contract="Video9.Demo3.Concurrency.ICalculatorServiceConcurrency"/>        <endpoint address="mex" binding="mexHttpBinding" contract="IMetadataExchange"/>      </service>    </services>        <behaviors>      <serviceBehaviors>        <behavior name="CalculatorServiceBehavior">          <!-- To avoid disclosing metadata information, set the value below to false and remove the metadata endpoint above before deployment -->          <serviceMetadata httpGetEnabled="true"/>          <!-- To receive exception details in faults for debugging purposes, set the value below to true.  Set to false before deployment to avoid disclosing exception information -->          <serviceDebug includeExceptionDetailInFaults="false"/>        </behavior>      </serviceBehaviors>    </behaviors>    <serviceHostingEnvironment multipleSiteBindingsEnabled="true" />  </system.serviceModel> <system.webServer>    <modules runAllManagedModulesForAllRequests="true"/>  </system.webServer>  </configuration>

5) 编译该项目后,把该服务部署到IIS上。这里我们要使用SVCUTIL.exe这个程序来生成客户端代理类,我们这里要使用async属性来控制生成的客户端代理类是可以异步调用的。

6) 生成完毕后,新建一个客户端程序Client,然后把“app.config”和"ClientCode.cs"拷贝到Client项目里。然后再客户端的Main方法中异步调用该服务。
using System;using System.Collections.Generic;using System.Linq;using System.Text;namespace Client{    class Program    {        static void Main(string[] args)        {            //生成代理类的实例            CalculatorServiceConcurrencyClient client = new CalculatorServiceConcurrencyClient();            //采用异步调用的方法来调用服务端服务,GetConcurrencyModeCallback为调用完服务端服务后所要执行的客户端反馈方法            client.BeginGetConcurrencyMode(GetConcurrencyModeCallback, client);            Console.WriteLine("我是在异步调用后的方法,但我会执行出结果,因为前面的方法是异步的,我不会等待他完成后在执行");            //BeginAdd            double value1 = 100.00D;            double value2 = 15.00D;            //这里异步调用的方法是 客户端代理类.Begin服务器端方法名(服务器端方法所需参数1,服务器端方法所需参数2,...,回调客户端的方法,客户端代理类的实例)。            IAsyncResult arrAdd = client.BeginAdd(value1, value2, AddCallback, client);            //BeginSubtract            IAsyncResult arrSubtract = client.BeginSubtract(value1, value2, SubtractCallback, client);            //BeginMultiply            IAsyncResult arrMultiply = client.BeginMultiply(value1, value2, MultiplyCallback, client);            //BeginDivide            IAsyncResult arrDivide = client.BeginDivide(value1, value2, DivideCallback, client);            //异步获得操作次数            client.BeginGetOperationCount(GetOperationCountCallback, client);            Console.ReadKey();                    }        //异步调用后执行的客户端方法        static void GetConcurrencyModeCallback(IAsyncResult ar)        {            //获得服务端的ConcurrencyMode的类型            string result = ((CalculatorServiceConcurrencyClient)ar.AsyncState).EndGetConcurrencyMode(ar);            Console.WriteLine("ConcurrencyMode : {0}", result);        }        //异步回调方法是使用" 返回值 回调方法名(IAsyncResult ar)" 来声明使用,ar就是调用服务器端后返回值的集合对象        static void AddCallback(IAsyncResult ar)        {            //使用 ((客户端代理类)ar.AsyncState).End服务器端所调用的方法名(ar);            double result = ((CalculatorServiceConcurrencyClient)ar.AsyncState).EndAdd(ar);            Console.WriteLine("Add Result : {0}", result);        }        static void SubtractCallback(IAsyncResult ar)        {            double result = ((CalculatorServiceConcurrencyClient)ar.AsyncState).EndSubtract(ar);            Console.WriteLine("Subtract : {0}", result);        }        static void MultiplyCallback(IAsyncResult ar)        {            double result = ((CalculatorServiceConcurrencyClient)ar.AsyncState).EndMultiply(ar);            Console.WriteLine("Multiply : {0}", result);        }        static void DivideCallback(IAsyncResult ar)        {            double result = ((CalculatorServiceConcurrencyClient)ar.AsyncState).EndDivide(ar);            Console.WriteLine("Divide : {0}", result);        }        static void GetOperationCountCallback(IAsyncResult ar)        {            double result = ((CalculatorServiceConcurrencyClient)ar.AsyncState).EndGetOperationCount(ar);            Console.WriteLine("OperationCount : {0}", result);        }    }}

7) 我们来运行一下客户端程序来测试一下,注意我们服务端的使用的ConcurrencyMode为Single也就是单线程,并且InstanceMode实例化模型也是Single,所以这是一个单实例单线程的服务,这里我们在加减乘除的每次运行的时候都让线程暂停1,2,3,4秒,并且让计算次数的时候暂停5秒。运行结果如下:

由此可以看出,由于客户端使用的异步调用所以并没有按照Thread暂停时间长端的顺序来执行,但是另一方面,当执行完前一个方法的时候光标都会在那闪烁(也就让线程暂停)相应设置的时间(1,2,3,4,5)。

8) 我们修改服务端的ConcurrencyMode为Multiple,也就说服务端是单实例多线程的,由于是异步调用,在服务端加减乘除以及获得操作数的方法都是同时在多线程里运行的,所以就可以在结果里看出,结果出来的顺序是加,减,乘,除,操作数, 而且他们之间的结果执行差距只有1秒钟,而且不管你运行多少次客户端程序,他们的执行顺序也不会发生变化,但是上面的使用ConcurrencyMode为Single的时候就会随机产生顺序。


源代码:  http://download.csdn.net/detail/eric_k1m/6412935