尽管ConcurrencyMode.Multiple

时间:2018-03-16 08:27:50

标签: c# .net multithreading wcf

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

服务应用代码:

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

服务端的控制台输出:

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

Out:3,Time:11:20:14.5184547,ThreadId:12

In:1,Time:11:20:14.6035310,ThreadId:12

Out:1,Time:11:20:15.6184362,ThreadId:12

In:2,Time:11:20:15.6184362,ThreadId:12

Out:2,Time:11:20:16.6342291,ThreadId:12

In:4,Time:11:20:16.6342291,ThreadId:12

Out:4,Time:11:20:17.6497805,ThreadId:12

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

Out:5,Time:11:20:18.6657260,ThreadId:12

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

Out:6,Time:11:20:19.6820159,ThreadId:12

2 个答案:

答案 0 :(得分:0)

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

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();
}

答案 1 :(得分:0)

这是因为您将在客户端限制为单个客户端实例,这显然不支持基本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绑定(尽管客户端有单个实例,但您可以在服务器上创建多个线程)