Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

WCF service is not multithreaded

I'm designing a WCF service used by a WPF application. The service will be used by 50 clients and hosted on a multi core server. That's why I would like it to be multi threaded.

This is how I declared it :

[ServiceContract(
    SessionMode = SessionMode.Required,
    Namespace = Constants.NameSpace,
    CallbackContract = typeof (ISaphirServiceCallback))]
public interface ISaphirService


[ServiceBehavior(ConcurrencyMode = ConcurrencyMode.Multiple, 
                InstanceContextMode=InstanceContextMode.PerSession)]
public partial class SaphirService : ISaphirService

And the server side configuration :

  <system.serviceModel>
    <bindings>
      <netTcpBinding>
        <binding name="NewBinding0" receiveTimeout="00:59:00" sendTimeout="00:59:00" maxReceivedMessageSize="2147483647" maxBufferPoolSize="20000000">
          <readerQuotas maxDepth="2147483647" maxStringContentLength="2147483647" maxArrayLength="2147483647" maxBytesPerRead="2147483647" maxNameTableCharCount="2147483647" />
          <reliableSession ordered="true" inactivityTimeout="00:30:00" enabled="true"/>
          <security mode="Message">
            <message clientCredentialType="UserName" />
          </security>
        </binding>
      </netTcpBinding>

      <customBinding>
        <binding name="ServicePECB2ServiceBinding">
          <textMessageEncoding messageVersion="Soap12WSAddressing10" />
          <httpsTransport />
        </binding>
      </customBinding>
    </bindings>
    <client>
      <endpoint address="https://qualiflps.services-ps.ameli.fr/lps" binding="customBinding" bindingConfiguration="ServicePECB2ServiceBinding" contract="ServiceReference1.ServicePECB2Service" name="ServicePECB2Service" />
    </client>

    <behaviors>
      <serviceBehaviors>
        <behavior name="NewBehavior0">
          <serviceThrottling maxConcurrentCalls="50" maxConcurrentSessions="50" maxConcurrentInstances="50"/>
          <serviceAuthorization serviceAuthorizationManagerType="Service.Authorizations.AuthorizationPolicy, Service">
            <authorizationPolicies>
              <add policyType="Service.Authorizations.AuthorizationPolicy, Service" />
            </authorizationPolicies>
          </serviceAuthorization>
          <serviceMetadata httpGetEnabled="true" httpGetUrl="http://127.0.0.1:80/Service" />
          <serviceDebug includeExceptionDetailInFaults="true" />
          <serviceCredentials>
            <serviceCertificate storeLocation="CurrentUser" storeName="TrustedPeople" x509FindType="FindBySubjectName" findValue="*****" />
            <userNameAuthentication userNamePasswordValidationMode="Custom" customUserNamePasswordValidatorType="Service.Authorizations.CustomValidator, Service" />
          </serviceCredentials>
        </behavior>
      </serviceBehaviors>
    </behaviors>
    <services>
      <service behaviorConfiguration="NewBehavior0" name="Service.Services.SaphirService">
        <endpoint address="basic" binding="netTcpBinding" bindingConfiguration="NewBinding0" contract="ServiceInterfaces.IServices.ISaphirService">
          <identity>
            <dns value="*****" />
          </identity>
        </endpoint>
      </service>
    </services>
  </system.serviceModel>

And here is the client side configuration :

  <system.serviceModel>
    <bindings>
      <netTcpBinding>
        <binding name="NetTcpBinding_ISaphirService" receiveTimeout="00:30:00" sendTimeout="00:05:00" maxReceivedMessageSize="2147483647" maxBufferPoolSize="20000000">
          <readerQuotas maxDepth="2147483647" maxStringContentLength="2147483647" maxArrayLength="2147483647" maxBytesPerRead="2147483647" maxNameTableCharCount="2147483647" />
          <reliableSession ordered="true" inactivityTimeout="00:30:00" enabled="true"/>
          <security mode="Message">
            <message clientCredentialType="UserName" />
          </security>
        </binding>
      </netTcpBinding>
    </bindings>
    <client>
      <endpoint address="http://****:4224/service/basic" binding="netTcpBinding" bindingConfiguration="NetTcpBinding_ISaphirService" contract="ISaphirService" name="NetTcpBinding_ISaphirService" behaviorConfiguration="CustomBehavior">
        <identity>
          <certificate encodedValue="****" />
        </identity>
      </endpoint>
    </client>
    <behaviors>
      <endpointBehaviors>
        <behavior name="CustomBehavior">
          <clientCredentials>
            <serviceCertificate>
              <authentication certificateValidationMode="PeerOrChainTrust" />
            </serviceCertificate>
          </clientCredentials>
        </behavior>
      </endpointBehaviors>
    </behaviors>
  </system.serviceModel>

The thing is, each request are processed on the same Thread. I checked a lot on the Internet but everything seems good to me...

Do you guys have any idea ?

Thanks !

like image 353
Guillaume Philipp Avatar asked Mar 21 '23 01:03

Guillaume Philipp


1 Answers

When opening a ServiceHost WCF captures the current SynchronizationContext, and uses it for all calls. WPF's synchronization context posts every call to the Dispatcher queue, which ends up executing on the UI thread.

You have two options:

  • Start the service on a different thread that doesn't have a synchronization context. This has the additional advantage of not blocking the UI thread waiting for the service to load. For example, you can use:

    Task.Run(() => serviceHost.Open());
    
  • Specify that the service should not use the synchronization context:

    [ServiceBehavior(UseSynchronizationContext = false)]
    

Note that if you modify UI objects in service methods, you might need to dispatch them back to the UI thread yourself.

like image 132
Eli Arbel Avatar answered Mar 29 '23 04:03

Eli Arbel