Reputation: 149
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,
Service's interface
namespace WCFSessionServer
{
[ServiceContract]
public interface IService
{
[OperationContract]
string GetDelayedResponse();
[OperationContract]
string GetDirectResponse();
}
}
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
}
}
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
Reputation: 139
Try to change the value of the attribute
UseSynchronizationContext=false
Upvotes: 2
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:
Client1
and Client2
Add Service Reference
to add a service reference to your servicethat 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