1

I’m writing a service that has a call that is relatively long running. The client needs to be able to make successive requests that run in parallel to each other and for some reason my service will not execute them concurrently unless the calls are executed from separate clients. I'm trying to figure out what configuration setting(s) I'm missing.

I’m using the netTcpBinding. My throttling configuration is:

<serviceThrottling maxConcurrentInstances="10" maxConcurrentCalls="10" maxConcurrentSessions="10"/>

The service contract:

[ServiceContract(CallbackContract=typeof(ICustomerServiceCallback))]
    public interface ICustomerService
    {
[OperationContract(IsOneWay = true)]
        void PrintCustomerHistory(string[] accountNumbers, 
            string destinationPath);
}

[ServiceBehavior(InstanceContextMode=InstanceContextMode.PerCall)]
    public class CustomerService : ICustomerService
    {

public void PrintCustomerHistory(string[] accountNumbers, 
            string destinationPath)
        {
//Do Stuff..
}
}

In the client, I’m making two successive asynchronous calls:

openProxy();

//call 1)
                proxy.PrintCustomerHistory(customerListOne, @"c:\DestinationOne\");

//call 2)
                proxy.PrintCustomerHistory(customerListTwo, @"c:\DestinationTwo\");

On the service, the second operation begins only after the first one ends. However, if I execute both calls from separate clients, they both execute concurrently by the service.

What am I missing? I had assumed that by marking my service class as “PerCall” that call 1 and call 2 each would receive their own InstanceContext and therefore execute concurrently on separate threads.

tshepang
  • 12,111
  • 21
  • 91
  • 136
Sean
  • 868
  • 9
  • 22

1 Answers1

2

You need to make the client calls asynchronous. If you're using VS 2012, you can enable the Task based asynchronous calls in the service reference, then call via:

var task1 = proxy.PrintCustomerHistoryAsync(customerListOne, @"c:\DestinationOne\");
var task2 = proxy.PrintCustomerHistoryAsync(customerListTwo, @"c:\DestinationTwo\");

// The two tasks are running, if you need to wait until they're done:
await Task.WhenAll(task1, task2);
Reed Copsey
  • 554,122
  • 78
  • 1,158
  • 1,373
  • Thanks for the response. I thought that the calls were already asynchronous by virtue of the following annotation: `[OperationContract(IsOneWay = true)]` I'm not yet familiar with "Task based asynchronous" calls and how they differ from what I already attempted. Do you have a reading suggestion? Thanks! – Sean Aug 22 '13 at 02:49
  • See MS docs: "Specifying that an operation is a one-way operation only means that there is no response message... If a client requires a non-blocking call, generate AsyncPattern operations: http://msdn.microsoft.com/en-us/library/system.servicemodel.operationcontractattribute.isoneway.aspx – Paul Keister Aug 22 '13 at 05:19
  • Thanks, Paul... Here's part of the source of my confusion. The guy who posted this question: [link](http://stackoverflow.com/questions/7122608/wcf-concurrencymode-single-and-instancecontextmode-percall) seems to have the opposite problem from me. Using PerCall/ConcurrencyMode.Single, he wants his service to execute each request as FIFO, but according to him, when calling from within a loop they execute concurrently. The solution suggested by ErnieL is to set MaxConcurrentInstances to 1. Anyway, I'll follow your link and give the AsyncPattern a try. – Sean Aug 22 '13 at 13:49
  • Can someone weigh in again? I've tried Reed's suggesting and this doesn't work. The problem is not that my client blocks on the first call until it's done, then allowing the second call to be made. The problem is that both calls are made one after the next, but the service ensures the first is completed before it begins processing the second. The client calls were already asynchronous (the client continued on without waiting on the service). Is there some config I'm missing on the service side? – Sean Aug 22 '13 at 18:47
  • 1
    @Sean On your server, why aren't you using `InstanceContextMode.Single` with `ConcurrencyMode.Multiple`? – Reed Copsey Aug 22 '13 at 18:52
  • My understanding (which might be completely wrong as I'm fairly new to WCF) was that ConcurrencyMode.Multiple with InanceContextMode.Single would mean that multiple threads would always access same InstanceContext. This isn't want I want. I want each call (whether from the same client or a different client) to get it's own InstanceContext and for each call to execute concurrently (up to the limits set by my throttling behavior). – Sean Aug 22 '13 at 19:03
  • To be more specific, I don't want to have to manage access to class-level variables in the service instance. I'd rather just have a new instance for each call. – Sean Aug 22 '13 at 19:05
  • Also, to reiterate, my service works just as I expect whenever the calls are being made from multiple clients simultaneously. The service appears to generate an instance context for both clients and they execute concurrently. I just can't figure out why the service insists on processing the requests in the order they were received when they are dispatched from the same client process -- without or without making them asynchronously. – Sean Aug 22 '13 at 19:11
  • @Sean If you use a different proxy, then does it happen? I suspect you want instance.PerCall and concurrency.Multiple, though the "right" way to handle this is (I believe) async handling on the server, too. – Reed Copsey Aug 22 '13 at 19:13
  • Yeah, it works correctly when I use a different proxy for each call. I didn't think that setting concurrency.Multiple with instance.PerCall would accomplish anything, since by definition each thread (call) gets it's own InstanceContext when using PerCall, whereas Concurrency.Multiple, I believe, is used with PerSession and means that within a given session multiple calls can be made against the same InstanceContext that lives throughout the lifespan of the Session. – Sean Aug 22 '13 at 19:23
  • @Sean Then I believe the problem is that the server methods aren't async, so they complete before the proxy can continue on... Have you tried setting ConcurrencyMode to multiple? – Reed Copsey Aug 22 '13 at 19:25
  • Ok, I will try ConcurrencyMode = multiple – Sean Aug 22 '13 at 19:27
  • Yes, you're right. ConcurrencyMode.Multiple did the trick. So does this mean that as long as I'm using PerCall, each call will be getting it's own instance and I don't have to worry whether another thread will gain access to the instance? – Sean Aug 22 '13 at 19:40
  • Thanks! I only started learning about instancing and concurrency in the WCF model this week. That clears things up a lot for me! – Sean Aug 22 '13 at 19:45
  • Reed, when you said up above that the "right" way to handle this is async handling on the server too, do you mean that I should manually write multi-threaded code with locking, etc? – Sean Aug 22 '13 at 19:47
  • @Sean If you're using 4.5, you can use the new async/await syntax to make this simpler. It doesn't need to be multithreaded - if there's any IO bound work, you can increase your scalability and make it much cleaner by being async – Reed Copsey Aug 22 '13 at 20:02