好得很程序员自学网

<tfoot draggable='sEl'></tfoot>

重入与回调并发(Reentrant & CallbackConcurrency )

重入与回调并发(Reentrant & CallbackConcurrency )

WCF中,并发是一个很影响服务性能的重要方面。通过ServiceBehaviorAttribute中的ConcurrencyMode可以设置服务的并发性。

对于双工通讯来说,服务对客户端的回调也是通过代理完成的。那么这又涉及到另外一个问题:回调客户端时,能否让回调服务也并发执行呢?WCF中定义了CallbackBehaviorAttribute ,可以通过它来设置回调服务的行为。它同样定义了ConcurrencyMode,可指定回调的并发模式,但它没有定义回调的实例模式,即InstanceContextMode。本文主要探讨服务的并发与回调服务的并发。

目录:

测试重入与回调并发  会话对 重入与回调并发  设置服务并发的ServiceBehaviorAttribute能用于回调服务吗?

通过实例进行说明:

契约定义:

 

[ServiceContract(CallbackContract =  typeof (IAddCallback))]    

public interface  IAdd
{
    [OperationContract]
     void  Add( int  x,  int  y);
}

 

回调契约定义:

 

     public   interface  IAddCallback   

    {
        [OperationContract]
         void  ShowResult( int  result);
    }

服务实现:

 

     public   class  AddService : IAdd  

    {
         private   readonly   int  counter;

         public  AddService()
        {
            counter++;
            Console.ResetColor();
            Console.WriteLine( string .Format( " AddService Construction function excute... counter is {0} " , counter));
        }

         #region  IAdd 成员

         public   void  Add( int  x,  int  y)
        {
             var  clientId = OperationContext.Current.IncomingMessageHeaders.GetHeader< int >(MessageWrapper.headerClientId,
                                                                                          MessageWrapper.headerNamespace);
            Console.ForegroundColor = ConsoleColor.Red;
            Console.WriteLine( string .Format( " Time:{0};ThreadId is :{1}.Request Id is {2} Add Method Invoked, " , DateTime.Now,
                                            Thread.CurrentThread.ManagedThreadId, clientId));
            Thread.Sleep( 5000 );
             int  result = x + y;
            MessageHeader< int > header =  new  MessageHeader< int >(clientId);
            System.ServiceModel.Channels.MessageHeader messageHeader =
                header.GetUntypedHeader(MessageWrapper.headerClientId,
                                        MessageWrapper.headerNamespace);
            OperationContext.Current.OutgoingMessageHeaders.Add(messageHeader);
            Console.WriteLine( string .Format( " Time: {1} Requst Id {0} begin Callback " ,clientId,DateTime.Now));
            IAddCallback callbackProxy= OperationContext.Current.GetCallbackChannel<IAddCallback>();
            callbackProxy.ShowResult(result);
            Console.WriteLine( string .Format( " Time: {1} Requst Id {0} Callback finished " , clientId, DateTime.Now));
            Console.WriteLine( string .Format( " result is : {0} " ,result));
            Thread.Sleep( 5000 );
            Console.WriteLine( " =========Excute finished========= " );
            Console.WriteLine();
        }

         #endregion
    }

服务配置:

    

     <system.serviceModel>

<services>
            <service name= " Service.AddService " >
                <endpoint address= " http://127.0.0.1:3636/AddService "  binding= " wsDualHttpBinding "  contract= " Contract.IAdd "  ></endpoint>
            </service>
        </services>
    </system.serviceModel>

 

客户端调用:

     public   class  AddCallback : IAddCallback
    {
         private   readonly   int  counter;
         public  AddCallback()
        {
            counter++;
            Console.WriteLine( string .Format( " AddCallback Construction function excute... counter is {0} " , counter));
        }

         #region  IAddCallback 成员

         public   void  ShowResult( int  result)
        {            
             int  callbackRequestId= OperationContext.Current.IncomingMessageHeaders.GetHeader< int >(MessageWrapper.headerClientId,
                                                                           MessageWrapper.headerNamespace);
            Console.ForegroundColor = ConsoleColor.Yellow;
            Console.WriteLine( string .Format( " Time: {1} ThreadId is :{2} Callback RequestId : {0} 开始执行回调 " , callbackRequestId, DateTime.Now,Thread.CurrentThread.ManagedThreadId));
            Thread.Sleep( 10000 );
            Console.WriteLine( string .Format( " Add result is {0} " , result));
            Console.WriteLine( string .Format( " Time: {1} ThreadId is :{2} Callback RequestId : {0} 回调结束 " , callbackRequestId, DateTime.Now, Thread.CurrentThread.ManagedThreadId));
            Console.WriteLine( " ================== " );
            Console.WriteLine();
        }
#endregion  
       }    


回调服务实现:

 

static   void  InvokeWithDiffrentProxy()
        {
            InstanceContext instanceContext= new  InstanceContext( new  AddCallback());
            DuplexChannelFactory<IAdd> factory =  new  DuplexChannelFactory<IAdd>(instanceContext, " AddService " );
             for  ( int  i =  0 ; i <  2 ; i++)
            {
                IAdd proxy = factory.CreateChannel();
                ThreadPool.QueueUserWorkItem( delegate
                                                 {
                                                      int  clientId = Interlocked.Increment( ref  index);
                                                      using  (
                                                         OperationContextScope contextScope =
                                                              new  OperationContextScope(proxy  as  IContextChannel))
                                                     {
                                                         MessageHeader< int > header =  new  MessageHeader< int >(clientId);
                                                         System.ServiceModel.Channels.MessageHeader messageHeader =
                                                             header.GetUntypedHeader(MessageWrapper.headerClientId,
                                                                                     MessageWrapper.headerNamespace);
                                                         OperationContext.Current.OutgoingMessageHeaders.Add(messageHeader);
                                                         proxy.Add( 1 ,  2 );
                                                     }
                                                 });
            }
        }

 

注意:如果XP下,使用wsDualHttpBinding作为绑定协议,需要对绑定做一下设置:

<bindings>
    <wsDualHttpBinding>
        <bind name= " wsDuplexBinding " >
            <clientBaseAddress  address= " http://127.0.0.1:6300/addCallbackService " >
        <bind>
        <wsDualHttpBinding>
</bindings>

 

下面做几个测试,观察一下服务端以及回调客户端的执行。

测试1:

服务端:ConcurrencyMode = ConcurrencyMode.Reentrant,InstanceContextMode = InstanceContextMode.Single

客户端:ConcurrencyMode = ConcurrencyMode.Single

回调服务休眠10秒

服务端输出:

 

客户端输出:

 

 

测试2:

服务端:ConcurrencyMode = ConcurrencyMode.Reentrant,InstanceContextMode = InstanceContextMode.Single

客户端:ConcurrencyMode = ConcurrencyMode.Single

回调服务休眠1秒

服务端输出:

 

客户端输出:

 

通过测试1、2的一组图的输出可知: Reentrant + Single 模式下:服务一次只能执行一个服务,当服务执行回调时,请求队列中的下一个请求将被服务执行。在回调服务的单实例模式下:回调也需要排队等待被处理。测试2的一组图显示:调用1的回调在时间为11:25:10的时候回调已经完成,但是回到服务端时,另外一个服务正占用锁,它只能等待。 这说明:当回调服务完成,回调客户端时,需要等待服务正在被处理的请求释放锁后才能被继续执行。

 

测试3:

服务端:ConcurrencyMode = ConcurrencyMode.Reentrant,InstanceContextMode = InstanceContextMode.PerSession

客户端:ConcurrencyMode = ConcurrencyMode.Single

 

服务端输出:

 

客户端输出:

 

通过上图分析知:服务对调用的请求通过并发的方式执行,而服务对客户端的回调还是串行的方式。

 

测试4:

服务端:ConcurrencyMode = ConcurrencyMode.Reentrant,InstanceContextMode = InstanceContextMode.PerCall

 客户端:ConcurrencyMode = ConcurrencyMode.Single

服务端输出:

 

客户端输出:

 

测试结果和测试3一样。

 

测试5:

服务端:ConcurrencyMode = ConcurrencyMode.Reentrant,InstanceContextMode = InstanceContextMode.Single

客户端:ConcurrencyMode = ConcurrencyMode.Multiple

服务端输出:

 

客户端输出:

 

通过上图分析知:服务对调用的请求通过串行的方式执行,而服务对客户端的回调是并行的方式。

 

测试6:

服务端:ConcurrencyMode = ConcurrencyMode.Reentrant,InstanceContextMode = InstanceContextMode.PerSession

客户端:ConcurrencyMode = ConcurrencyMode.Multiple

服务端输出:

 

客户端输出: 

 

通过上图分析知:服务对调用的请求以及服务对客户端的回调是并行的方式。

 

测试7:

服务端:ConcurrencyMode = ConcurrencyMode.Reentrant,InstanceContextMode = InstanceContextMode.PerSession

客户端:ConcurrencyMode = ConcurrencyMode.Multiple

服务端输出:

 

客户端输出: 

 

通过上图分析知:服务对调用的请求以及服务对客户端的回调是并行的方式。

 

测试8会话模式 :

服务端:ConcurrencyMode = ConcurrencyMode.Reentrant,InstanceContextMode = InstanceContextMode.PerSession

客户端:ConcurrencyMode = ConcurrencyMode.Single

服务端输出:

 

客户端输出:

 

通过上图分析知:服务对调用的请求并发进行处理,服务对客户端的回调以串行的方式处理。

 

测试9会话模式:

服务端:ConcurrencyMode = ConcurrencyMode.Reentrant,InstanceContextMode = InstanceContextMode.PerSession

客户端:ConcurrencyMode = ConcurrencyMode.Multiple

服务端输出:

 

客户端输出:

 

服务对调用的请求以及服务对客户端的回调是并行的方式。

 

如果将ServiceBehaviorAttribute用于回调类型,结果会怎样呢?

测试10会话模式 :

服务端:ConcurrencyMode = ConcurrencyMode.Reentrant,InstanceContextMode = InstanceContextMode.PerSession

客户端:ServiceBehavior(ConcurrencyMode = ConcurrencyMode.Single)

服务端输出:

 

客户端输出:

 

通过上图分析知:服务对调用的请求并发进行处理,服务对客户端的回调以并行的方式处理。

测试11会话模式:

服务端:ConcurrencyMode = ConcurrencyMode.Reentrant,InstanceContextMode = InstanceContextMode.PerSession

客户端:ServiceBehavior(ConcurrencyMode = ConcurrencyMode.Multiple,InstanceContextMode = InstanceContextMode.PerCall)

服务端输出:

客户端输出:

 

 

 

测试12会话模式:

服务端:ConcurrencyMode = ConcurrencyMode.Reentrant,InstanceContextMode = InstanceContextMode.PerSession

客户端:ServiceBehavior(ConcurrencyMode = ConcurrencyMode.Multiple,InstanceContextMode = InstanceContextMode.PerSession)

服务端输出:

 

客户端输出:

 

测试10、测试11、测试12分析知:服务对调用的请求并发进行处理;服务对客户端的回调以串行的方式处理,这点可以从回调的构造,以及打印出来的时间两方面得到验证。

对回调使用ServiceBehaviorAttribute无效。

 

测试13会话模式:

服务端:ConcurrencyMode = ConcurrencyMode.Reentrant,InstanceContextMode = InstanceContextMode.PerSession

客户端:ConcurrencyMode = ConcurrencyMode.Single

同一客户端并发调用服务,将调用代码做如下更改:

(proxy as ICommunicationObject).Open();

服务端输出:

 

客户端输出:

测试14会话模式:

ConcurrencyMode = ConcurrencyMode.Reentrant,InstanceContextMode = InstanceContextMode.PerSession

ConcurrencyMode = ConcurrencyMode.Multiple

同一客户端并发调用服务,将调用代码做如下更改:

(proxy as ICommunicationObject).Open();

 

服务端输出:

 

客户端输出:

测试15:

ConcurrencyMode = ConcurrencyMode.Reentrant,InstanceContextMode = InstanceContextMode.PerSession

ConcurrencyMode = ConcurrencyMode.Multiple

同一客户端并发调用服务,将调用代码做如下更改:

(proxy as ICommunicationObject).Open();

服务端输出:

 

客户端输出: 

 

测试16:

ConcurrencyMode = ConcurrencyMode.Reentrant,InstanceContextMode = InstanceContextMode.PerCall

ConcurrencyMode = ConcurrencyMode.Multiple

同一客户端并发调用服务,将调用代码做如下更改:

(proxy as ICommunicationObject).Open();

服务端输出:

 

客户端输出:

测试17:

ConcurrencyMode = ConcurrencyMode.Reentrant,InstanceContextMode = InstanceContextMode.Single

ConcurrencyMode = ConcurrencyMode.Multiple

同一客户端并发调用服务,将调用代码做如下更改:

(proxy as ICommunicationObject).Open();

服务端输出:

 

客户端输出:

 

测试18:

服务端:ConcurrencyMode = ConcurrencyMode.Reentrant,InstanceContextMode = InstanceContextMode.PerCall

客户端:ServiceBehavior(ConcurrencyMode = ConcurrencyMode.Multiple,InstanceContextMode = InstanceContextMode.PerCall) 

服务端输出:

 

客户端输出:

 

  由测试10、11、12、18这些测试得出如下结论: ServiceBehaviorAttribute对回调服务的并发设置无效。

 会话模式下,不同客户端代理对可重入的服务总是以并发的方式执行;相同客户端对可重入的服务总是以串行的方式执行,而无论怎样,回调服务总是以串行的方式执行。

 

分类:  WCF基础

代码下载: https://files.cnblogs.com/tyb1222/WCFCallbackConcurrency.rar

作者: Leo_wl

    

出处: http://www.cnblogs.com/Leo_wl/

    

本文版权归作者和博客园共有,欢迎转载,但未经作者同意必须保留此段声明,且在文章页面明显位置给出原文连接,否则保留追究法律责任的权利。

版权信息

查看更多关于重入与回调并发(Reentrant & CallbackConcurrency )的详细内容...

  阅读:42次