在wcf服务中实现并发的正确方法是什么?

时间:2012-12-17 14:46:11

标签: multithreading wcf c#-4.0 service concurrency

我正在尝试了解wcf中的并发性并从here下载了示例代码并对测试双向调用进行了一些更改。令我惊讶的是,我无法看到并发对双向调用的影响(虽然我可以看到单向调用的并发性)。 这是WCF并发模型的工作方式吗? (要么) 我做错了什么?

这是服务代码。

[ServiceContract]
public interface IHelloWorldService
{
    [OperationContract(IsOneWay=true)]
    void Call(string ClientName);

    [OperationContract]
    string GetData(int value);

    [OperationContract]
    CompositeType GetDataUsingDataContract(CompositeType composite);
}
[DataContract]
public class CompositeType
{
    bool boolValue = true;
    string stringValue = "Hello ";

    [DataMember]
    public bool BoolValue
    {
        get { return boolValue; }
        set { boolValue = value; }
    }

    [DataMember]
    public string StringValue
    {
        get { return stringValue; }
        set { stringValue = value; }
    }
}
[ServiceBehavior(InstanceContextMode=InstanceContextMode.Single, ConcurrencyMode = ConcurrencyMode.Multiple)]
public class HelloWorldService : IHelloWorldService
{
    public static int counter;

    public HelloWorldService()
    {
        counter++;
    }

    public void Call(string ClientName)
    {            
        Console.WriteLine("Instance:" + counter.ToString() + " Thread:" + Thread.CurrentThread.ManagedThreadId.ToString() + " Time:" + DateTime.Now.ToString() + "\n\n");
        Thread.Sleep(5000);
    }

    public string GetData(int value)
    {
        Console.WriteLine("Instance:" + counter.ToString() + " Thread:" + Thread.CurrentThread.ManagedThreadId.ToString() + " Time:" + DateTime.Now.ToString() + "\n\n");
        Thread.Sleep(5000);
        return value.ToString();
    }

    public CompositeType GetDataUsingDataContract(CompositeType composite)
    {
        Console.WriteLine("Instance:" + counter.ToString() + " Thread:" + Thread.CurrentThread.ManagedThreadId.ToString() + " Time:" + DateTime.Now.ToString() + "\n\n");
        Thread.Sleep(5000);
        return composite;
    }
}

这是服务托管代码。

class Program
{
    static void Main(string[] args)
    {
        //Create a URI to serve as the base address

        //Uri httpUrl = new Uri("net.tcp://localhost:8001/HelloWorld");
        Uri httpUrl = new Uri("http://localhost:8010/MyService/HelloWorld");

        //Create ServiceHost
        ServiceHost host
        = new ServiceHost(typeof(ClassLibrary1.HelloWorldService), httpUrl);

        //Add a service endpoint
        host.AddServiceEndpoint(typeof(ClassLibrary1.IHelloWorldService)
        , new WSHttpBinding(), "");

        //Enable metadata exchange
        ServiceMetadataBehavior smb = new ServiceMetadataBehavior();
        smb.HttpGetEnabled = true;
        host.Description.Behaviors.Add(smb);

        ServiceThrottlingBehavior stb = new ServiceThrottlingBehavior();
        stb.MaxConcurrentCalls = 100;
        stb.MaxConcurrentInstances = 100;
        stb.MaxConcurrentSessions = 100;
        host.Description.Behaviors.Add(stb);


        //Start the Service
        host.Open();

        Console.WriteLine("Service is host at " + DateTime.Now.ToString());
        Console.WriteLine("Host is running... Press <Enter> key to stop");
        Console.ReadLine();

    }
}

这是客户端代码。

class Program
{
    static int m_NumberOfWorkers = 10;
    static readonly object m_Locker = new object();
    static bool flag_GO = false;
    static Stopwatch m_OverAllStopwatch = new Stopwatch();
    static ConcurrentBag<Stopwatch> m_bagIndividualStopwatch = new ConcurrentBag<Stopwatch>();
    static int m_CompletedWorkers = 0;
    static ServiceReference1.HelloWorldServiceClient m_objProxy;
    static int m_KindOfMethod;
    static void Main(string[] args)
    {
        while(true)
        {
            try
            {
                flag_GO = false;

                Console.WriteLine("Enter number of concurrent clients:");
                m_NumberOfWorkers = Int32.Parse(Console.ReadLine());

                Console.WriteLine("Kind of method (1: One way, 2: Two way 3: Two way using data contract):");
                m_KindOfMethod = Int32.Parse(Console.ReadLine());

                // Create Workers
                List<Thread> lstThreads = new List<Thread>();
                for (int i = 0; i < m_NumberOfWorkers; ++i)
                {
                    lstThreads.Add(new Thread(WaitOnPulse));
                }

                // Start Workers
                for (int i = 0; i < lstThreads.Count; ++i)
                {
                    lstThreads[i].Start();
                }

                m_objProxy = new ServiceReference1.HelloWorldServiceClient();

                m_OverAllStopwatch.Restart();

                // Signal all workers
                lock (m_Locker)
                {
                    flag_GO = true;
                    Monitor.PulseAll(m_Locker);
                }

                // Wait all workers to finish
                for (int i = 0; i < lstThreads.Count; ++i)
                {
                    lstThreads[i].Join();
                }

                m_objProxy.Close();
                m_objProxy = null;
            }
            catch
            {
                return;
            }
        }            
    }

    private static void WaitOnPulse()
    {
        lock (m_Locker)
        {
            while (!flag_GO) Monitor.Wait(m_Locker);
        }
        TestWhatEverYouWant();
        IamDone();
    }

    private static void TestWhatEverYouWant()
    {
        Stopwatch stopWatch = Stopwatch.StartNew();
        //Thread.Sleep(1000);
        switch (m_KindOfMethod)
        {
            case 1:
                m_objProxy.Call(m_NumberOfWorkers.ToString() + "Client Calls");
                break;
            case 2:
                m_objProxy.GetData(m_NumberOfWorkers);
                break;
            case 3:
                ServiceReference1.CompositeType objData = new ServiceReference1.CompositeType();
                m_objProxy.GetDataUsingDataContract(objData);
                break;
        }
        stopWatch.Stop();
        m_bagIndividualStopwatch.Add(stopWatch);
    }

    private static void IamDone()
    {
        Interlocked.Increment(ref m_CompletedWorkers);
        // Summarize results if all workers are done
        if (Interlocked.CompareExchange(ref m_CompletedWorkers, 0, m_NumberOfWorkers) == m_NumberOfWorkers)
        {
            m_OverAllStopwatch.Stop();
            Console.WriteLine("OverAll Elapsed Time: {0}", m_OverAllStopwatch.ElapsedMilliseconds);
            Stopwatch stopWatch;
            while (m_bagIndividualStopwatch.TryTake(out stopWatch))
            //foreach (Stopwatch stopWatch in m_bagIndividualStopwatch)
            {
                Console.WriteLine("Individual Elapsed Time: {0}", stopWatch.ElapsedMilliseconds);
            }
        }
    }
}

这是Cleint追踪:

    Enter number of concurrent clients:
    8
    Kind of method (1: One way, 2: Two way 3: Two way using data contract):
    2
    OverAll Elapsed Time: 42022
    Individual Elapsed Time: 42021
    Individual Elapsed Time: 37013
    Individual Elapsed Time: 32008
    Individual Elapsed Time: 26987
    Individual Elapsed Time: 21981
    Individual Elapsed Time: 16980
    Individual Elapsed Time: 11968
    Individual Elapsed Time: 6985

这是服务器跟踪:

    Instance:1 Thread:6 Time:12/17/2012 8:09:29 PM


    Instance:1 Thread:5 Time:12/17/2012 8:09:34 PM


    Instance:1 Thread:7 Time:12/17/2012 8:09:39 PM


    Instance:1 Thread:7 Time:12/17/2012 8:09:44 PM


    Instance:1 Thread:5 Time:12/17/2012 8:09:49 PM


    Instance:1 Thread:7 Time:12/17/2012 8:09:54 PM


    Instance:1 Thread:5 Time:12/17/2012 8:09:59 PM


    Instance:1 Thread:7 Time:12/17/2012 8:10:04 PM

对于这些结果,您可以清楚地看到请求是按顺序处理的。理想情况下,我期待所有8个并发请求将在5秒内完成。但是花了大约42秒完成。

2 个答案:

答案 0 :(得分:0)

我的代码中的问题是代理的使用方式。我为所有并发客户端创建了一个代理,并且所有对服务的调用都只通过此代理进行。因此,所有这些呼叫都在通道中排队。 为每个客户创建一个代理 ,模拟wcf负载测试的方式,解决了问题。

答案 1 :(得分:-1)

我仍然认为问题在于设置;代理和源自每个代理的线程数,链接解释得很好。

另请看下面的链接;可能是测试客户端可能有问题。 Seeking WCF Duplex "TwoWay" Subscribe+Callback Example