【问题标题】:WCF service handles incoming requests in a single thread, despite ConcurrencyMode.MultipleWCF 服务在单个线程中处理传入请求,尽管 ConcurrencyMode.Multiple
【发布时间】:2018-08-25 05:57:51
【问题描述】:

在控制台应用程序和同一个客户端中有一个简单的 WCF 服务。客户端同时(在不同的线程中)向服务器发送 N 个请求。预计处理并发请求的服务器会同时分配多个线程,并在第二次停机后(由 Thread.Sleep (1000) 专门制作)同时将答案返回给客户端,但这不会发生。尽管 ServiceBehavior 属性设置为 ConcurrencyMode.Multiple,但服务在一个线程中处理所有请求(这在屏幕截图中的 ThreadId 中可以看到)。

服务应用代码:

namespace Server
{
  using System;
  using System.ServiceModel;
  using System.Threading;

  [ServiceContract]
  public interface IServer
  {
    [OperationContract]
    int GetResult(int value);
  }

  [ServiceBehavior(ConcurrencyMode = ConcurrencyMode.Multiple,  
    InstanceContextMode = InstanceContextMode.Single)]
  public class Server: IServer
  {
    public int GetResult(int value)
    {
      Console.WriteLine("In: {0}, Time: {1}, ThreadId: {2}", 
                        value, DateTime.Now.TimeOfDay, Thread.CurrentThread.ManagedThreadId);

      Thread.Sleep(1000);

      Console.WriteLine("Out: {0}, Time: {1}, ThreadId: {2}",
                        value, DateTime.Now.TimeOfDay, Thread.CurrentThread.ManagedThreadId);
      return value;
    }
  }


  class Program
  {
    static void Main()
    {
      var host = new ServiceHost(new Server());
      host.Open();

      Console.WriteLine("Service started");

      Console.ReadLine();
    }
  }
}

客户端应用代码:

using System;

namespace Server
{
  using System.ServiceModel;

  [ServiceContract]
  public interface IServer
  {
    [OperationContract]
    int GetResult(int value);
  }
}

namespace Client
{
  using System.ServiceModel;
  using System.Threading;

  using Server;

  class Program
  {
    static object lockObj = new object();

    static void Main()
    {
      ChannelFactory<IServer> factory = new ChannelFactory<IServer>("defaultEndPoint");
      IServer channel = factory.CreateChannel();


      const int threadCount = 6;
      int value = 0;

      for (int i = 0; i < threadCount; i++)
      {
        new Thread(state =>
          {
            int n;
            lock (lockObj)
            {
              value++;
              n = value;
            }
            Console.WriteLine("Send value = {0}, Time = {1}", n, DateTime.Now.TimeOfDay);
            n = channel.GetResult(n);
            Console.WriteLine("Response value = {0}, Time = {1}", n, DateTime.Now.TimeOfDay);
          }).Start();
      }

      Console.ReadLine();
    }
  }
}

服务配置:

<?xml version="1.0" encoding="utf-8"?>
<configuration>
  <system.serviceModel>
    <behaviors>
      <serviceBehaviors>
        <behavior name="DefaultBehavior">
          <serviceMetadata httpGetEnabled="true"/>
          <serviceThrottling maxConcurrentCalls="1000" maxConcurrentInstances="1000" maxConcurrentSessions="1000"/>
        </behavior>
      </serviceBehaviors>
    </behaviors>
    <services>
      <service name="Server.Server" behaviorConfiguration="DefaultBehavior">
        <endpoint contract="Server.IServer" binding="basicHttpBinding" address=""/>
        <endpoint address="mex" binding="mexHttpBinding" contract="IMetadataExchange"/>
        <host>
          <baseAddresses>
            <add baseAddress="http://localhost:7803/"/>
          </baseAddresses>
        </host>
      </service>
    </services>
  </system.serviceModel>
<startup><supportedRuntime version="v4.0" sku=".NETFramework,Version=v4.5"/></startup></configuration>

客户端的控制台输出:

发送值 = 1,时间 = 11:20:13.1335555

发送值 = 3,时间 = 11:20:13.1335555

发送值 = 2,时间 = 11:20:13.1335555

发送值 = 4,时间 = 11:20:13.1335555

发送值 = 5,时间 = 11:20:13.1335555

发送值 = 6,时间 = 11:20:13.1335555

响应值 = 3,时间 = 11:20:14.6191583

响应值 = 1,时间 = 11:20:15.6184362

响应值 = 2,时间 = 11:20:16.6342291

响应值 = 4,时间 = 11:20:17.6497805

响应值 = 5,时间 = 11:20:18.6657260

响应值 = 6,时间 = 11:20:19.6820159

服务端的控制台输出:

在:3,时间:11:20:13.5030783,ThreadId:12

输出:3,时间:11:20:14.5184547,ThreadId:12

在:1,时间:11:20:14.6035310,ThreadId:12

输出:1,时间:11:20:15.6184362,ThreadId:12

在:2,时间:11:20:15.6184362,ThreadId:12

输出:2,时间:11:20:16.6342291,ThreadId:12

在:4,时间:11:20:16.6342291,ThreadId:12

输出:4,时间:11:20:17.6497805,ThreadId:12

在:5,时间:11:20:17.6497805,ThreadId:12

输出:5,时间:11:20:18.6657260,ThreadId:12

在:6,时间:11:20:18.6657260,ThreadId:12

输出:6,时间:11:20:19.6820159,ThreadId:12

【问题讨论】:

标签: c# .net multithreading wcf


【解决方案1】:

这是因为您将自己在客户端限制为单个客户端实例,该实例显然不支持基本 http 绑定的多个并发调用。

这里有两个选项:

  1. 通过为每个线程创建一个额外的通道来扩展客户端:

    ChannelFactory<IServer> factory = new ChannelFactory<IServer>();
    
    const int threadCount = 6;
    int value = 0; 
    
    for (int i = 0; i < threadCount; i++)
    {
      new Thread(state =>
      {
        IServer channel = factory.CreateChannel();
    
        int n;
        lock (lockObj)
        {
            value++;
            n = value;
        }
    
        ...
    
  2. 切换到似乎支持不同并发的 WS 绑定(尽管客户端只有一个实例,但您可以在服务器上创建多个线程)

【讨论】:

  • 感谢您的帮助!当服务器和客户端在同一台计算机上时,这可以正常工作。但是当我在本地网络的不同计算机上运行它们时,服务器开始只分配两个线程。
  • 服务端控制台输出如下图 In: 4, Time: 15:50:24.86, ThreadId: 7 In: 2, Time: 15:50:24.86, ThreadId: 4 Out: 2时间:15:50:25.89,ThreadId:4出:4时间:15:50:25.89,ThreadId:7 In:6,时间:15:50:25.94,ThreadId:7 In:5,时间:15:50 :25.94, ThreadId: 4 Out: 5 Time: 15:50:26.95, ThreadId: 4 Out: 6 Time: 15:50:26.95, ThreadId: 7 可能是什么原因?
  • 这可能是因为默认的出站连接限制限制了您的客户端。尝试将System.Net.ServicePointManager.DefaultConnectionLimit 设置为更高的值。
【解决方案2】:

我完全意识到出了什么问题。在客户端,所有线程都使用通道的一份副本。并且有必要在每个线程中创建一个单独的通道。像这样:

ChannelFactory<IServer> factory = new ChannelFactory<IServer>("defaultEndPoint");
// !!! IServer channel = factory.CreateChannel();

const int threadCount = 6;
int value = 0;

for (int i = 0; i < threadCount; i++)
{
  new Thread(state =>
    {
      int n;
      lock (lockObj)
      {
        value++;
        n = value;
      }
      IServer channel = factory.CreateChannel(); // !!! 
      Console.WriteLine("Send value = {0}, Time = {1}", n, DateTime.Now.TimeOfDay);
      n = channel.GetResult(n);
      Console.WriteLine("                                          Response value = {0}, Time = {1}", n, DateTime.Now.TimeOfDay);
    }).Start();
}

【讨论】:

    猜你喜欢
    • 2012-03-08
    • 2012-07-29
    • 1970-01-01
    • 2022-07-20
    • 2014-02-05
    • 2011-12-21
    • 2013-07-26
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多