如果我们使用PerCall实例化,并发模式是否相关?

时间:2013-10-08 17:17:49

标签: asp.net wcf

在我的示例WCF控制台应用程序中,我已将服务类的服务行为设置为

[ServiceBehavior(ConcurrencyMode = ConcurrencyMode.Single,InstanceContextMode = InstanceContextMode.PerCall)]

并使用相同的代理对象调用服务的多个操作,如下所示。

             proxy = new ConcurrencyServiceClient("ConcurrencyService_Tcp");

             proxy.BuyStock("MSFT", 100);

             proxy.BuyStock("GOOG", 50);

             proxy.BuyStock("F", 500);

             proxy.SellStock("AAPL", 100);

             proxy.SellStock("ORCL", 300);

             proxy.SellStock("GM", 75);

在上面的例子中,它按顺序调用每个操作,但如果我选择并发模式为“Multiple”而不是“Single”,它将同时执行操作。

我在许多地方都读到'ConcurrencyMode对PerCall服务没有影响,因为根据定义,来自客户端的每个调用都会被分配一个带有自己线程的新服务实例:PerCall总是有效的ConcurrencyMode.Single。'

我正在使用netTcpBinding绑定。

问题确实让我感到害怕,请告诉我这个行为。

谢谢!

1 个答案:

答案 0 :(得分:2)

  

如果我们使用PerCall实例化,并发模式是否相关?

通常不是。如果您总是为ConcurrencyMode.PerCall的每个调用创建一个新实例,则每个实例中只有一个线程...因此本身就是线程安全的。

唯一的例外是,如果服务“A”对服务“B”进行同步呼叫,则服务“B”将呼叫返回到服务“A”的同一实例。这特别是ConcurrencyMode.Reentrant案例。

查看您发布的程序输出:

令人困惑的是您正在使用相同的代理对象。序列化在客户端而不是服务上。代理是线程安全的,但一次只向服务发送一个请求。

我在下面写了一个命令行程序,你可以运行:

  • 服务方法将在返回前休眠1秒
  • 客户端方法执行两次循环。
    • 在每个循环中,它创建10个线程来调用服务十次。
    • 在第一个循环中,它为每个调用创建一个新代理 - 您会看到并发。
    • 在第二个循环中,它跨线程共享相同的代理 - 并且调用是序列化的。

当我在ConcurrencyMode.Multiple和ConcurrencyMode.Single之间切换时,我没有看到输出有任何差异。

using System;
using System.ServiceModel;
using System.ServiceModel.Description;
using System.ServiceModel.Dispatcher;
using System.Threading.Tasks;

using System.Collections.Generic;

namespace ConsoleWCF
{
    [ServiceContract]
    public interface ISimple
    {
        [OperationContract]
        int GiveItBack(int i);
    }

    //// Different ConcurrencyMode does NOT change the output.
    //[ServiceBehavior(ConcurrencyMode=ConcurrencyMode.Single, InstanceContextMode=InstanceContextMode.PerCall)]
    [ServiceBehavior(ConcurrencyMode = ConcurrencyMode.Multiple, InstanceContextMode = InstanceContextMode.PerCall)]
    public class SimpleService : ISimple
    {
        public int GiveItBack(int i)
        {
            Console.WriteLine("Return " + i);
            System.Threading.Thread.Sleep(1000);
            return i;
        }
    }

    public static class Program
    {
        static void Main(string[] args)
        {
            ServiceHost simpleHost = new ServiceHost(typeof(SimpleService), new Uri("http://localhost/Simple"));
            simpleHost.Open();

            ChannelFactory<ISimple> factory = new ChannelFactory<ISimple>(simpleHost.Description.Endpoints[0]);
            List<Task> tasks = new List<Task>();


            Console.WriteLine("{0}{0}Start proxy per call....", Environment.NewLine);
            var stopwatch = System.Diagnostics.Stopwatch.StartNew();
            for (int i = 0; i < 10; i++)
            {
                int value = i;
                tasks.Add(Task.Factory.StartNew(() =>
                    {
                        // proxy per call...
                        ISimple proxy = factory.CreateChannel();    // <-------- new Proxy
                        Console.WriteLine("Client    - Sending " + value);
                        int response = proxy.GiveItBack(value);
                        Console.WriteLine("Client    - Got back " + response);

                        ((ICommunicationObject)proxy).Shutdown();
                    }));
            }

            Task.WaitAll(tasks.ToArray());
            Console.WriteLine("Finished in {0} msec", stopwatch.ElapsedMilliseconds);

            Console.WriteLine("{0}{0}Start Shared proxy....", Environment.NewLine);
            ISimple sharedProxy = factory.CreateChannel();    // <-------- one one Proxy
            stopwatch = System.Diagnostics.Stopwatch.StartNew();

            for (int i = 0; i < 10; i++)
            {
                int value = i;
                tasks.Add(Task.Factory.StartNew(() =>
                {
                    // proxy per call...
                    Console.WriteLine("Client    - Sending " + value);
                    int response = sharedProxy.GiveItBack(value);
                    Console.WriteLine("Client    - Got back " + response);
                }));
            }

            Task.WaitAll(tasks.ToArray());

            ((ICommunicationObject)sharedProxy).Shutdown();

            Console.WriteLine("Finished in {0} msec", stopwatch.ElapsedMilliseconds);

            Console.WriteLine("Press ENTER to close the host once you see 'ALL DONE'.");
            Console.ReadLine();

            factory.Shutdown();
            simpleHost.Shutdown();
        }
    }

    public static class Extensions
    {
        static public void Shutdown(this ICommunicationObject obj)
        {
            try
            {
                obj.Close();
            }
            catch (Exception ex)
            {
                Console.WriteLine("Shutdown exception: {0}", ex.Message);
                obj.Abort();
            }
        }
    }
}

示例输出:

Start proxy per call....
Client    - Sending 0
Client    - Sending 1
Client    - Sending 2
Client    - Sending 3
Return 1
Return 2
Return 0
Return 3
Client    - Sending 4
Return 4
Client    - Got back 2
Client    - Got back 1
Client    - Got back 0
Client    - Got back 3
Client    - Sending 5
Client    - Sending 6
Client    - Sending 7
Client    - Sending 8
Return 5
Return 6
Return 7
Return 8
Client    - Sending 9
Client    - Got back 4
Return 9
Client    - Got back 6
Client    - Got back 8
Client    - Got back 5
Client    - Got back 7
Client    - Got back 9
Finished in 3009 msec


Start Shared proxy....
Client    - Sending 0
Client    - Sending 3
Client    - Sending 5
Client    - Sending 2
Client    - Sending 1
Client    - Sending 4
Return 0
Client    - Sending 6
Client    - Got back 0
Client    - Sending 7
Return 3
Client    - Sending 8
Client    - Got back 3
Client    - Sending 9
Return 5
Client    - Got back 5
Return 2
Client    - Got back 2
Return 1
Client    - Got back 1
Return 4
Client    - Got back 4
Return 6
Client    - Got back 6
Return 7
Client    - Got back 7
Return 8
Client    - Got back 8
Return 9
Client    - Got back 9
Finished in 10027 msec
Press ENTER to close the host once you see 'ALL DONE'.

为什么创建单独代理的循环仍然需要3秒才能运行而不是1秒可能是由于某个地方的限制或线程限制...而不是限制它的WCF行为。