tags:

views:

61

answers:

3

Hi all,

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.

The question is, How to 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 & GetDirectResponse AT THE SAME TIME AND GET THE "FAST" TEXT BEFORE THE 8 SECONDS ENDS.

Thanks in advance,

A: 

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.

marc_s
Hi,Unfortunately, after i changed what you recommended above, the behavior still processed sequentially.Any advices?
Sabri Ghaith
You'll need to show us more of your code for any further diagnosis. What does your web.config/app.config look like? How are you calling this WCF service?
marc_s
Ok, code attached.
Sabri Ghaith
@Sabri Ghaith: I would have loved to see *how* you are calling that service from your client where you say things are still serialized....
marc_s
@ marc_s , see the code i just posted and give me your feedback or give me a solution for my prove of concept to get multi-threaded wcf service.
Sabri Ghaith
@ marc_s.Well, I just saw your post and willing to test it. Well, I was running two instances of same windows application (client) to call different methods.
Sabri Ghaith
I did what you posted recently but with no result. Do you try it?
Sabri Ghaith
A: 

CONTINUED ...

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>
Sabri Ghaith
You should rather edit your question than posting an answer.
Martin Fabik
Thanks for advice, Martin
Sabri Ghaith
A: 

CONTINUED ... 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();
        }
    }
}
Sabri Ghaith