Sabri Ghaith
Sabri Ghaith

Reputation: 149

Does WCF support Multi-threading itself?

I developed a proof of concept application that query if WCF support multi-threading.

Now, all what I did is creating a service contract marked with

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

with two operations to get fixed texts. The first method do a Thread.Sleep for 8 seconds to make the response delayed and the other one return data directly.

The issue I faced was when I run two instances of client application and request from the first client the method with delay and request the other method from the second client, I got a sequential response.

How can I get the response from the service while the service is busy with another request?

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

I need to call the methods GetDelayedResponse and GetDirectResponse at the same time and get the "fast" text before the 8 seconds ends.


Hosting application code

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

Below is the code I implement it to test the case:

Server side classes,

  1. Service's interface

    namespace WCFSessionServer
    {
        [ServiceContract]
        public interface IService
    
        {
            [OperationContract]
            string GetDelayedResponse();
    
           [OperationContract]
           string GetDirectResponse();
        }
     }
    
  2. Implementation class

    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. Server-side 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>
    

Client-side 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>

Upvotes: 11

Views: 21799

Answers (2)

EvgenOrel
EvgenOrel

Reputation: 139

Try to change the value of the attribute

UseSynchronizationContext=false 

Upvotes: 2

marc_s
marc_s

Reputation: 755321

Well, by defining your service to be

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

you're basically defining your service class to be a singleton (InstanceContextMode.Single) which certainly isn't the best approach. By defining it as ConcurrencyMode.Multiple, you make it a multi-threaded singleton - which puts a lot of burden of making sure your code is 200% thread-safe onto your own shoulders.

My recommendation would be to mark your service implementation class as per-call.

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

With this approach, the WCF runtime itself will spin up as many service instance classes as needed, to handle your requests. In your example, the WCF runtime will create and start up two instances of ServiceImplementation, one for each request, and handle the calls concurrently. The big advantage is: since each service instance class only serves one request, you don't need to worry about concurrency management in your code - you're inside a "single-threaded" class and the WCF runtime handles all the issues related to having multiple requests and handling them appropriately.

Update: you're still not showing how your are creating your client-side service proxy, and how you're calling your service. You've posted just about all the server-side code - but not a shred of client-side code.

OK, here's how to do it:

  • spin up your service host and make sure it's running
  • in Visual Studio, create two separate console application projects for your clients - call them Client1 and Client2
  • in both those new client projects, use Add Service Reference to add a service reference to your service
  • that will create a bunch of files under that "Service Reference" globe

  • You now need to instantiate an instance of the client-side proxy in both your client projects:

     In Client1:
         var instance1 = new ServiceImplementationClient();
    
     In Client2:
         var instance2 = new ServiceImplementationClient();
    
  • Client1 will call your first method GetDelayedResponse, while Client2 will call GetDirectResponse:

     In Client1:
         instance1.GetDelayedResponse();
    
     In Client2:
         instance2.GetDirectResponse();
    
  • if you run those two apps simultaneously, you should see that Client2 returns right away, while Client1 will wait for those 8 seconds.

If you have two totally separate clients, and they will get a totally separate service instance on the server, they are totally independant of one another and won't be serializing their calls and won't be blocking each other.

Upvotes: 17

Related Questions