Thursday, June 10, 2010

"Processor affinity" when making WCF calls from multiple threads

I'm having a strange problem with my WCF client/server. From a "fresh" client I fire off 100 calls from a single thread and my server processes all the calls on a single processor. Great. From a fresh client again, I fire off 100 calls from 10 threads (10 each), it still processes them all on one processor. However, if I start a fresh client and fire off 100 calls on one thread then immediately follow that by 100 calls over 10 threads, I see all 8 processors handling calls.

Fresh client:
100 calls from 1 thread on client puts all calls on only 1 logical processor on server

Fresh client:
10 calls from 10 threads on client puts all calls on only 1 logical processor on server

Fresh client:
100 calls from 1 thread on client immediately followed by
100 calls from 10 threads on client puts calls successfully on ALL 8 logical processors on server

So why does a fresh client's calls always get serviced by a single logical processor? Why does the second set of calls get serviced by all logical processors?

Sample code from client:
public partial class MyAgentClient
{
    static System.ServiceModel.Channels.Binding _binding;
    static System.ServiceModel.EndpointAddress _endpoint;
    static ChannelFactory<IMyAgent> _channelFactory;
    static IMyAgent _channel;

    static MyAgentClient _instance;
    public static MyAgentClient Instance
    {
        get
        {
            return _instance;
        }
    }

    static MyAgentClient()
    {
        _binding = WCFSettings.CurrentBinding;
        _endpoint = new EndpointAddress(WCFSettings.CurrentURI + "/MyAgent");
        _instance = new MyAgentClient();
        _channelFactory = new ChannelFactory<IMyAgent>(_binding, _endpoint);
        _channel = _channelFactory.CreateChannel();
    }

    public MyReturnData GetSomeData(string p1, string p2, string p3, SomeEnum p4, int p5, int p6)
    {
        try
        {
            return _channel.GetSomeData(p1, p2, p3, p4, p5, p6);
        }
        catch (Exception ex)
        {
            // Log the exception.
        }
    }
}
Sample code from server:
[ServiceContract]
public interface IMyAgent
{
    [OperationContract]
    MyReturnData GetSomeData(string p1, string p2, string p3, SomeEnum p4, int p5, int p6);
}

[ServiceBehavior(ConcurrencyMode = ConcurrencyMode.Multiple, InstanceContextMode = InstanceContextMode.PerCall, UseSynchronizationContext = false)]
public class MyAgent : IMyAgent
{
    public MyReturnData GetSomeData(string p1, string p2, string p3, SomeEnum p4, int p5, int p6)
    {
        try
        {
            return MyCode.StaticMethods.GetSomeData(enterpriseCode, companyNumber, salespersonFilter, leadCategoryType, startIndex, pageSize);
        }
        catch (Exception ex)
        {
            // Log the exception.
        }
    }
}
WCF binding:
public static CustomBinding NewTcpBinding
{
    get
    {

        TimeSpan oneMinute = new TimeSpan(0, 1, 0);
        TimeSpan tenMinutes = new TimeSpan(0, 10, 0);

        ReliableSessionBindingElement reliableSession = new ReliableSessionBindingElement();
        reliableSession.MaxPendingChannels = 512;
        reliableSession.Ordered = false;
        reliableSession.FlowControlEnabled = false;
        reliableSession.MaxTransferWindowSize = 4096;
        reliableSession.InactivityTimeout = tenMinutes;

        BinaryMessageEncodingBindingElement binaryEncoding = new BinaryMessageEncodingBindingElement();
        binaryEncoding.ReaderQuotas.MaxStringContentLength = 2147483646;
        binaryEncoding.ReaderQuotas.MaxArrayLength = 50000000;

        TcpTransportBindingElement tcpTransport = new TcpTransportBindingElement();
        tcpTransport.MaxReceivedMessageSize = 2147483646;
        tcpTransport.MaxBufferSize = 2147483646;
        tcpTransport.MaxBufferPoolSize = 524288;
        tcpTransport.TransferMode = TransferMode.Buffered;

        BindingElementCollection elements = new BindingElementCollection();
        elements.Add(reliableSession);
        elements.Add(binaryEncoding);
        elements.Add(tcpTransport);

        CustomBinding binding = new CustomBinding(elements)
        {
            Name = "TcpBinding",
            CloseTimeout = oneMinute,
            OpenTimeout = oneMinute,
            ReceiveTimeout = tenMinutes,
            SendTimeout = oneMinute
        };
        return binding;
    }
}

1 comment:

Anonymous said...

Informative post!! Thank you for sharing it with us!!
signs sheffield