views:

143

answers:

1

I have a WCF service client and it was generated using the /async argument with svcutil.exe, so it uses the asynchronous programming model. When I make multiple asynchronous requests with a single client though it serializes the execution of those requests.

Here is an example that shows how I am executing the parellel requests.

[TestMethod]
public void AsyncTest()
{
    var req1 = Helpers.Request["Symbol"];
    var req2 = Helpers.Request["Summary"];

    using(var client = new SoapClientAsync(_TcpBinding, _TestRunConfig.ServiceAddress))
    {
        IAsyncResult[] results = new[]
            {
                client.BeginExec(req1, new AsyncCallback(asyncComplete), new MyAsyncState { Client = client, Request = req1 }),
                client.BeginExec(req2, new AsyncCallback(asyncComplete), new MyAsyncState { Client = client, Request = req2 })
            };

        WaitHandle[] waits = results.Select(i => (MyAsyncState)i.AsyncState).Select(i => i.WaitEvent).ToArray();
        WaitHandle.WaitAll(waits);

        var states = results.Select(i => (MyAsyncState)i.AsyncState);

        foreach (var state in states)
        {
            Console.WriteLine(string.Format("client {0}", state.Watch.ElapsedMilliseconds));
            Console.WriteLine(string.Format("server {0}", state.Response.ExecutionSummary.Single(i => i.Message == "Total").Duration));
        }
    }
}

private void asyncRequestComplete(IAsyncResult ar)
{
    TestAsyncState state = (MyAsyncState)ar.AsyncState;
    state.Response = state.Client.EndExec(ar);
    state.Watch.Stop();
    state.WaitEvent.Set();
}

Here is the output that I am seeing:

client 26
server 24

client 53
server 26

The server is reporting a consistent execution time of ~25ms. But it is pretty obviously serializing the execution of each request on the client side.

What is the proper way to execute parellel requests against a web service endpoint using WCF?

+1  A: 

The answer is quite simply to use more than one instance of the client and issue a single request on each. Soap clients are extremely lightweight, so I wouldn't hesitate to use as many of them as makes sense for your scenario.

Ben M
I've also been told that I should create a ChannelFactory<> and use Channel instances directly. Thoughts?
spoon16
A `ChannelFactory<T>` doesn't give you builtin async methods, so you'd have to roll that yourself... which is easy enough with `ThreadPool`. If that's fine with you, I don't see any downside to that approach.
Ben M
The contract that is being implemented is an async contract (all OperationContract instances are marked with AsyncPattern=true). Wouldn't using that contract in the ChannelFactory enable async execution without resorting to manually leveraging the ThreadPool?
spoon16
Yes, it would. I had assumed you'd be using `ChannelFactory<T>` with the non-async contract interface; but if you're still using `svcutil.exe` to generate the async contract on the client side, that'll work.
Ben M