2010-07-20 15 views
7

Desarrollé una aplicación de prueba de concepto que pregunta si WCF soporta multi-threading.¿WCF es compatible con Multi-threading?

Ahora, todo lo que hice es la creación de un contrato de servicio marcado con

[ServiceBehavior(InstanceContextMode = InstanceContextMode.Single, 
        ConcurrencyMode = ConcurrencyMode.Multiple, 
        UseSynchronizationContext = true)] 

con dos operaciones para obtener textos fijos. El primer método hace un Thread.Sleep durante 8 segundos para retrasar la respuesta y el otro devolver datos directamente.

El problema al que me enfrenté fue cuando ejecuto dos instancias de aplicación cliente y solicito del primer cliente el método con retraso y solicito el otro método del segundo cliente, obtengo una respuesta secuencial.

¿Cómo puedo obtener la respuesta del servicio mientras el servicio está ocupado con otra solicitud?

namespace WCFSyncService 
{ 
    [ServiceBehavior(InstanceContextMode = InstanceContextMode.Single)], 
       ConcurrencyMode = ConcurrencyMode.Multiple, 
       UseSynchronizationContext = true)] 

    public class ServiceImplementation : IService 

    { 
     public ServiceImplementation() 
     { 
     } 

     #region IService Members 

     public string GetDelayedResponse() 
     { 
      System.Threading.Thread.Sleep(8000); 
      return "Slow"; 
     } 

     public string GetDirectResponse() 
     { 
      return "Fast"; 
     } 

     #endregion 
    } 
} 

Tengo que llamar a los métodos GetDelayedResponse y GetDirectResponse al mismo tiempo y obtener el texto "rápida" antes de que termine el 8 segundos.


Hosting código de la aplicación

namespace ServiceHostApplication 
{ 
    public partial class frmMain : Form 
    { 
     private WCFSessionServer.IService oService; 

     public frmMain() 
     { 
      InitializeComponent(); 
     } 

     private void btnStartService_Click(object sender, EventArgs e) 
     { 
      ServiceHost objSvcHost; 

      oService = new WCFSessionServer.ServiceImplementation(); 
     objSvcHost = new ServiceHost(typeof(WCFSessionServer.ServiceImplementation)); 
      objSvcHost.Open(); 
     } 
    } 
} 

A continuación se muestra el código que ponerlo en práctica para probar el caso: las clases laterales

Server,

  1. Servicio de interfaz

    namespace WCFSessionServer 
    { 
        [ServiceContract] 
        public interface IService 
    
        { 
         [OperationContract] 
         string GetDelayedResponse(); 
    
         [OperationContract] 
         string GetDirectResponse(); 
        } 
    } 
    
  2. clase de implementación

    namespace WCFSessionServer 
    { 
        [ServiceBehavior(
            InstanceContextMode = InstanceContextMode.PerCall, 
            ConcurrencyMode = ConcurrencyMode.Multiple, 
            UseSynchronizationContext = true)] 
        public class ServiceImplementation : IService 
        { 
         public ServiceImplementation() 
          { 
          } 
    
         #region Service Members 
         public string GetDelayedResponse() 
          { 
          System.Threading.Thread.Sleep(8000); 
          return "Slow"; 
          } 
    
         public string GetDirectResponse() 
         { 
          return "Fast"; 
         } 
         #endregion 
        } 
    } 
    
  3. -lado del servidor app.config

    <system.serviceModel> 
    <services> 
        <service 
          behaviorConfiguration = "WCFSessionServer.IService" 
          name = "WCFSessionServer.ServiceImplementation" > 
        <endpoint address="http://localhost:2020/SessionService/basic/" 
          behaviorConfiguration="WCFSessionServer.IService" 
          binding="basicHttpBinding" 
          name="BasicHttpBinding_IService" 
          bindingName="myBasicHttpBinding" 
          contract="WCFSessionServer.IService" /> 
        <endpoint address="mex" 
          binding="mexHttpBinding" 
          contract="IMetadataExchange" /> 
        <host> 
        <baseAddresses> 
        <add baseAddress="http://localhost:2020/SessionService/" /> 
        </baseAddresses> 
        </host> 
    </service> 
    </services> 
         <behaviors> 
         <endpointBehaviors> 
         <behavior name="TimeOut"> 
         <callbackTimeouts transactionTimeout="00:00:02"/> 
         </behavior> 
        <behavior name="WCFSessionServer.IService" > 
         <dataContractSerializer maxItemsInObjectGraph="2147483647" /> 
        </behavior> 
        </endpointBehaviors> 
        <serviceBehaviors> 
        <behavior name="WCFSessionServer.IService"> 
         <serviceThrottling maxConcurrentCalls="10" 
              maxConcurrentSessions="10" 
              maxConcurrentInstances="10"/> 
          <dataContractSerializer maxItemsInObjectGraph="2147483647" /> 
          <serviceMetadata httpGetEnabled="True"/> 
          <serviceDebug includeExceptionDetailInFaults="True" /> 
         </behavior> 
         </serviceBehaviors> 
        </behaviors> 
        </system.serviceModel> 
    

-lado del cliente app.config

<system.serviceModel> 
      <bindings> 
       <basicHttpBinding> 
        <binding name="BasicHttpBinding_IService" 
          closeTimeout="00:01:00" 
          openTimeout="00:01:00" 
          receiveTimeout="00:10:00" 
          sendTimeout="00:01:00" 
          allowCookies="false" 
          bypassProxyOnLocal="false" 
          hostNameComparisonMode="StrongWildcard" 
          maxBufferSize="65536" 
          maxBufferPoolSize="524288" 
          maxReceivedMessageSize="65536" 
          messageEncoding="Text" 
          textEncoding="utf-8" 
          transferMode="Buffered" 
          useDefaultWebProxy="true"> 
          <readerQuotas maxDepth="32" 
             maxStringContentLength="8192" 
             maxArrayLength="16384" 
             maxBytesPerRead="4096" 
             maxNameTableCharCount="16384" /> 
        <security mode="None"> 
        <transport 
           clientCredentialType="None" 
           proxyCredentialType="None" 
           realm="" /> 
        <message 
          clientCredentialType="UserName" 
          algorithmSuite="Default" /> 
       </security> 
      </binding> 
     </basicHttpBinding> 
    </bindings> 
    <client> 
     <endpoint address="http://localhost:2020/SessionService/basic/" 
        binding="basicHttpBinding" 
        bindingConfiguration="BasicHttpBinding_IService" 
        contract="SessionServiceProxy.IService" 
        name="BasicHttpBinding_IService" /> 
    </client> 
</system.serviceModel> 
+10

¡DEJAR DE GRITAR, POR FAVOR! –

Respuesta

14

Pues bien, mediante la definición de su servicio para ser

[ServiceBehavior(InstanceContextMode=InstanceContextMode.Single, 
       ConcurrencyMode=ConcurrencyMode.Multiple, 
       UseSynchronizationContext=true)] 

que está básicamente la definición de su clase de servicio a ser un producto único (InstanceContextMode.Single) que sin duda no es el mejor enfoque. Al definirlo como ConcurrencyMode.Multiple, lo convierte en un singleton multiproceso, lo que supone una gran carga para garantizar que su código sea 200% seguro para hilos sobre sus propios hombros.

Mi recomendación sería marcar su clase de implementación del servicio como por llamada.

[ServiceBehavior(InstanceContextMode=InstanceContextMode.PerCall, 
       ConcurrencyMode=ConcurrencyMode.Single)] 

Con este enfoque, el propio tiempo de ejecución de WCF girar tantas clases instancia de servicio, según sea necesario, para manejar sus solicitudes.En su ejemplo, el tiempo de ejecución de WCF creará e iniciará dos instancias de ServiceImplementation, una para cada solicitud, y manejará las llamadas al mismo tiempo. La gran ventaja es que, dado que cada clase de instancia de servicio solo atiende una solicitud, no necesita preocuparse por la administración de simultaneidad en su código: está dentro de una clase de "subproceso único" y el tiempo de ejecución de WCF maneja todos los problemas relacionados con tener múltiples solicitudes y manejarlas apropiadamente.

Actualización: todavía estás no mostrando cómo su están creando su proxy de servicio del lado del cliente, y cómo se está llamando a su servicio. Ha publicado casi todo el código del lado del servidor, pero no una parte del código del lado del cliente.

OK, aquí está cómo hacerlo:

  • giro su host de servicio y asegúrese de que está funcionando
  • en Visual Studio, crear dos proyectos de aplicación de consola separadas para sus clientes - los llaman Client1 y Client2
  • tanto en los nuevos proyectos de los clientes, utilice Add Service Reference para agregar una referencia de servicio a su servicio
  • que va a crear un montón de archivos en virtud de ese mundo "servicio de referencia"

  • ahora tiene que crear una instancia del proxy del cliente, tanto en sus proyectos de clientes:

    In Client1: 
        var instance1 = new ServiceImplementationClient(); 
    
    In Client2: 
        var instance2 = new ServiceImplementationClient(); 
    
  • Client1 llamará a su primer método GetDelayedResponse, mientras Client2 llamarán GetDirectResponse:

    In Client1: 
        instance1.GetDelayedResponse(); 
    
    In Client2: 
        instance2.GetDirectResponse(); 
    
  • si ejecuta esas dos aplicaciones al mismo tiempo, debería ver que Client2 regresa de inmediato, mientras que Client1 esperará esos 8 segundos.

Si tiene dos clientes totalmente separadas, y que obtendrá una instancia de servicio totalmente independiente en el servidor, que son totalmente independientes el uno del otro y no serán serialización de sus llamadas y no serán bloqueo de cada otro.

+0

Hola, Desafortunadamente, después de cambiar lo que recomendó anteriormente, el comportamiento aún se procesa secuencialmente. ¿Algún consejo? –

+0

Deberá mostrarnos más de su código para cualquier diagnóstico posterior. ¿Cómo se ve tu web.config/app.config? ¿Cómo llamas a este servicio de WCF? –

+0

Ok, código adjunto. –