2010-07-20 17 views
7

Ho sviluppato un'applicazione proof of concept che interroga se WCF supporta il multi-threading.WCF supporta il multi-threading stesso?

Ora, tutto quello che ho fatto è la creazione di un contratto di servizio contrassegnato da

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

con due operazioni per ottenere testi fissi. Il primo metodo esegue uno Thread.Sleep per 8 secondi per ritardare la risposta e l'altro per restituire i dati direttamente.

Il problema che ho dovuto affrontare è quando eseguo due istanze di applicazione client e richiedo dal primo client il metodo con ritardo e richiedo l'altro metodo dal secondo client, ho ottenuto una risposta sequenziale.

Come posso ottenere la risposta dal servizio mentre il servizio è occupato con un'altra richiesta?

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 
    } 
} 

ho bisogno di chiamare i metodi GetDelayedResponse e GetDirectResponse allo stesso tempo e ottenere il testo "veloce" prima della fine del 8 secondi.


Hosting codice dell'applicazione

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(); 
     } 
    } 
} 

seguito è riportato il codice di implemento per testare il caso: le classi laterali

Server,

  1. servizio di interfaccia

    namespace WCFSessionServer 
    { 
        [ServiceContract] 
        public interface IService 
    
        { 
         [OperationContract] 
         string GetDelayedResponse(); 
    
         [OperationContract] 
         string GetDirectResponse(); 
        } 
    } 
    
  2. classe di implementazione

    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. lato server 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> 
    

lato client 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

STOP YELLING, PER FAVORE! –

risposta

14

Beh, definendo il vostro servizio essere

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

sei fondamentalmente definendo la classe di servizio per essere un Singleton (InstanceContextMode.Single), che certamente non è l'approccio migliore. Definendolo come ConcurrencyMode.Multiple, lo si rende un singleton multi-threaded, il che pone un sacco di onere per assicurarsi che il codice sia sicuro al 200% da thread sulle proprie spalle.

La mia raccomandazione sarebbe quella di contrassegnare la classe di implementazione del servizio come chiamata.

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

Con questo approccio, il runtime WCF si filerà come molte classi istanza di servizio, se necessario, per gestire le vostre richieste.Nell'esempio, il runtime di WCF creerà e avvierà due istanze di ServiceImplementation, una per ogni richiesta e gestirà le chiamate contemporaneamente. Il grande vantaggio è: poiché ogni classe di istanza di servizio serve solo una richiesta, non è necessario preoccuparsi della gestione della concorrenza nel codice: si è all'interno di una classe "single-threaded" e il runtime di WCF gestisce tutti i problemi relativi a avere più richieste e gestirle in modo appropriato.

Aggiornamento: sei ancora non che mostra come il tuo sta creando il proxy di servizio sul lato client, e come si sta chiamando il vostro servizio. Hai pubblicato quasi tutto il codice lato server, ma non un brandello di codice lato client.

OK, ecco come fare:

  • spin up vostro ospite servizio e assicurarsi che sia in esecuzione
  • in Visual Studio, creare due progetti di applicazione console separata per i vostri clienti - chiamarli Client1 e Client2
  • in entrambi i nuovi progetti dei clienti, utilizzare Add Service Reference per aggiungere un riferimento al servizio per il vostro servizio
  • che creerà un gruppo di file in quel globo "service Reference"

  • è ora necessario creare un'istanza di un'istanza del proxy lato client in entrambi i vostri progetti dei clienti:

    In Client1: 
        var instance1 = new ServiceImplementationClient(); 
    
    In Client2: 
        var instance2 = new ServiceImplementationClient(); 
    
  • Client1 chiamerà il primo metodo GetDelayedResponse, mentre Client2 chiamerà GetDirectResponse:

    In Client1: 
        instance1.GetDelayedResponse(); 
    
    In Client2: 
        instance2.GetDirectResponse(); 
    
  • se si eseguono queste due applicazioni contemporaneamente, si dovrebbe vedere che Client2 restituisce subito, mentre Client1 attenderà quegli 8 secondi.

Se si dispone di due client totalmente separati, e che riceveranno un'istanza del servizio totalmente separata sul server, sono totalmente indipendenti l'uno dall'altro e non saranno serializzazione loro chiamate e non saranno bloccando ogni altro.

+0

Ciao, Sfortunatamente, dopo aver modificato ciò che si consigliava in precedenza, il comportamento veniva elaborato in sequenza. Eventuali consigli? –

+0

Dovrai mostrarci altro codice per ulteriori diagnosi. Che aspetto ha il tuo web.config/app.config? Come stai chiamando questo servizio WCF? –

+0

Ok, codice allegato. –