1

在我的示例 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。”</p>

我正在使用 netTcpBinding 绑定。

问题真的要了我的命,请告诉我这种行为。

谢谢!

4

1 回答 1

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 行为。

于 2013-10-08T21:41:20.437 回答