WCF 本身是否支持多线程? [英] Does WCF support Multi-threading itself?

查看:32
本文介绍了WCF 本身是否支持多线程?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我开发了一个概念验证应用程序,用于查询 WCF 是否支持多线程.

I developed a proof of concept application that query if WCF support multi-threading.

现在,我所做的就是创建一个标有

Now, all what I did is creating a service contract marked with

 [ServiceBehavior(InstanceContextMode = InstanceContextMode.Single,
                  ConcurrencyMode = ConcurrencyMode.Multiple, 
                  UseSynchronizationContext = true)]

通过两个操作来获取固定文本.第一种方法做一个Thread.Sleep 8 秒,使响应延迟,另一种方法直接返回数据.

with two operations to get fixed texts. The first method do a Thread.Sleep for 8 seconds to make the response delayed and the other one return data directly.

我面临的问题是,当我运行两个客户端应用程序实例并延迟从第一个客户端请求该方法并从第二个客户端请求另一个方法时,我得到了顺序响应.

The issue I faced was when I run two instances of client application and request from the first client the method with delay and request the other method from the second client, I got a sequential response.

当服务正忙于另一个请求时,我如何从该服务获得响应?

How can I get the response from the service while the service is busy with another request?

 namespace WCFSyncService
 {
    [ServiceBehavior(InstanceContextMode = InstanceContextMode.Single)], 
                 ConcurrencyMode = ConcurrencyMode.Multiple, 
                 UseSynchronizationContext = true)]

    public class ServiceImplementation : IService

    {
        public ServiceImplementation()
        {
        }

        #region IService Members

        public string GetDelayedResponse()
        {
            System.Threading.Thread.Sleep(8000);
            return "Slow";
        }

        public string GetDirectResponse()
        {
            return "Fast";
        }

        #endregion
    }
}

我需要同时调用方法 GetDelayedResponseGetDirectResponse 并在 8 秒结束前获取快速"文本.

I need to call the methods GetDelayedResponse and GetDirectResponse at the same time and get the "fast" text before the 8 seconds ends.

托管应用程序代码

namespace ServiceHostApplication
{
    public partial class frmMain : Form
    {
        private WCFSessionServer.IService oService;

        public frmMain()
        {
            InitializeComponent();
        }

        private void btnStartService_Click(object sender, EventArgs e)
        {
            ServiceHost objSvcHost;

            oService = new WCFSessionServer.ServiceImplementation();
         objSvcHost = new ServiceHost( typeof(WCFSessionServer.ServiceImplementation));
            objSvcHost.Open();
        }
    }
}

<小时>

下面是我实现它来测试案例的代码:


Below is the code I implement it to test the case:

服务器端类,

  1. 服务接口

  1. Service's interface

namespace WCFSessionServer
{
    [ServiceContract]
    public interface IService

    {
        [OperationContract]
        string GetDelayedResponse();

       [OperationContract]
       string GetDirectResponse();
    }
 }

  • 实现类

  • Implementation class

    namespace WCFSessionServer
    {
       [ServiceBehavior(
                        InstanceContextMode = InstanceContextMode.PerCall,
                        ConcurrencyMode =   ConcurrencyMode.Multiple,
                        UseSynchronizationContext =  true)]
        public class ServiceImplementation : IService
         {
             public ServiceImplementation()
                {
                }
    
             #region Service Members
             public string GetDelayedResponse()
                {
                 System.Threading.Thread.Sleep(8000);
                 return "Slow";
                }
    
             public string GetDirectResponse()
             {
                 return "Fast";
             }
             #endregion
         }
     }
    

  • 服务器端 app.config

  • Server-side app.config

    <system.serviceModel>
     <services>
      <service 
                behaviorConfiguration = "WCFSessionServer.IService"  
                name = "WCFSessionServer.ServiceImplementation" >
      <endpoint address="http://localhost:2020/SessionService/basic/"
                behaviorConfiguration="WCFSessionServer.IService"
                binding="basicHttpBinding"
                name="BasicHttpBinding_IService"
                bindingName="myBasicHttpBinding"
                contract="WCFSessionServer.IService" />
      <endpoint address="mex"
                binding="mexHttpBinding"
                contract="IMetadataExchange" />
      <host>
       <baseAddresses>
         <add baseAddress="http://localhost:2020/SessionService/" />
       </baseAddresses>
      </host>
    </service>
     </services>
          <behaviors>
          <endpointBehaviors>
           <behavior name="TimeOut">
            <callbackTimeouts transactionTimeout="00:00:02"/>
          </behavior>
         <behavior name="WCFSessionServer.IService" >
           <dataContractSerializer maxItemsInObjectGraph="2147483647" />
         </behavior>
       </endpointBehaviors>
       <serviceBehaviors>
        <behavior name="WCFSessionServer.IService">
           <serviceThrottling    maxConcurrentCalls="10"
                                 maxConcurrentSessions="10"
                                 maxConcurrentInstances="10"/>
               <dataContractSerializer maxItemsInObjectGraph="2147483647" />
               <serviceMetadata httpGetEnabled="True"/> 
               <serviceDebug includeExceptionDetailInFaults="True" />
             </behavior>
           </serviceBehaviors>
         </behaviors>
       </system.serviceModel>
    

  • 客户端 app.config

    Client-side app.config

        <system.serviceModel>
              <bindings>
                  <basicHttpBinding>
                      <binding name="BasicHttpBinding_IService"
                               closeTimeout="00:01:00"
                               openTimeout="00:01:00"
                               receiveTimeout="00:10:00"
                               sendTimeout="00:01:00"
                               allowCookies="false"
                               bypassProxyOnLocal="false"
                               hostNameComparisonMode="StrongWildcard"
                               maxBufferSize="65536"
                               maxBufferPoolSize="524288"
                               maxReceivedMessageSize="65536"
                               messageEncoding="Text"
                               textEncoding="utf-8"
                               transferMode="Buffered"
                               useDefaultWebProxy="true">
                               <readerQuotas maxDepth="32"
                                             maxStringContentLength="8192"
                                             maxArrayLength="16384"
                                             maxBytesPerRead="4096"
                                             maxNameTableCharCount="16384" />
                        <security mode="None">
                        <transport
                                  clientCredentialType="None"
                                  proxyCredentialType="None"
                                  realm="" />
                        <message 
                                 clientCredentialType="UserName"
                                 algorithmSuite="Default" />
                    </security>
                </binding>
            </basicHttpBinding>
        </bindings>
        <client>
            <endpoint address="http://localhost:2020/SessionService/basic/"
                      binding="basicHttpBinding"
                      bindingConfiguration="BasicHttpBinding_IService"
                      contract="SessionServiceProxy.IService"
                      name="BasicHttpBinding_IService" />
        </client>
    </system.serviceModel>
    

    推荐答案

    好吧,通过定义您的服务

    Well, by defining your service to be

    [ServiceBehavior(InstanceContextMode=InstanceContextMode.Single, 
                     ConcurrencyMode=ConcurrencyMode.Multiple, 
                     UseSynchronizationContext=true)] 
    

    您基本上将服务类定义为单例 (InstanceContextMode.Single),这当然不是最佳方法.通过将其定义为 ConcurrencyMode.Multiple,您可以使其成为多线程单例 - 这将确保您的代码 200% 线程安全的重担放在您自己的肩上.

    you're basically defining your service class to be a singleton (InstanceContextMode.Single) which certainly isn't the best approach. By defining it as ConcurrencyMode.Multiple, you make it a multi-threaded singleton - which puts a lot of burden of making sure your code is 200% thread-safe onto your own shoulders.

    我的建议是将您的服务实现类标记为每次调用.

    My recommendation would be to mark your service implementation class as per-call.

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

    使用这种方法,WCF 运行时本身将根据需要启动尽可能多的服务实例类来处理您的请求.在您的示例中,WCF 运行时将创建和启动 ServiceImplementation 的两个实例,每个请求一个实例,并同时处理调用.最大的优点是:由于每个服务实例类只处理一个请求,因此您无需担心代码中的并发管理——您在一个单线程"类中,WCF 运行时处理与有多个请求并适当处理它们.

    With this approach, the WCF runtime itself will spin up as many service instance classes as needed, to handle your requests. In your example, the WCF runtime will create and start up two instances of ServiceImplementation, one for each request, and handle the calls concurrently. The big advantage is: since each service instance class only serves one request, you don't need to worry about concurrency management in your code - you're inside a "single-threaded" class and the WCF runtime handles all the issues related to having multiple requests and handling them appropriately.

    更新:您仍然没有展示您如何创建客户端服务代理以及如何调用您的服务.您已经发布了几乎所有的服务器端代码 - 但没有发布任何客户端代码.

    Update: you're still not showing how your are creating your client-side service proxy, and how you're calling your service. You've posted just about all the server-side code - but not a shred of client-side code.

    好的,这是怎么做的:

    • 启动您的服务主机并确保它正在运行
    • 在 Visual Studio 中,为您的客户端创建两个单独的控制台应用程序项目 - 将它们称为 Client1Client2
    • 在这两个新客户端项目中,使用添加服务引用向您的服务添加服务引用
    • 这将在服务参考"地球下创建一堆文件

    • spin up your service host and make sure it's running
    • in Visual Studio, create two separate console application projects for your clients - call them Client1 and Client2
    • in both those new client projects, use Add Service Reference to add a service reference to your service
    • that will create a bunch of files under that "Service Reference" globe

    您现在需要在两个客户端项目中实例化一个客户端代理实例:

    You now need to instantiate an instance of the client-side proxy in both your client projects:

     In Client1:
         var instance1 = new ServiceImplementationClient();
    
     In Client2:
         var instance2 = new ServiceImplementationClient();
    

  • Client1 将调用您的第一个方法 GetDelayedResponse,而 Client2 将调用 GetDirectResponse:

  • Client1 will call your first method GetDelayedResponse, while Client2 will call GetDirectResponse:

     In Client1:
         instance1.GetDelayedResponse();
    
     In Client2:
         instance2.GetDirectResponse();
    

  • 如果您同时运行这两个应用程序,您应该会看到 Client2 立即返回,而 Client1 将等待这 8 秒.

  • if you run those two apps simultaneously, you should see that Client2 returns right away, while Client1 will wait for those 8 seconds.

    如果您有两个完全独立的客户端,并且它们将在服务器上获得一个完全独立的服务实例,则它们彼此完全独立,不会序列化它们的调用,也不会相互阻塞.

    If you have two totally separate clients, and they will get a totally separate service instance on the server, they are totally independant of one another and won't be serializing their calls and won't be blocking each other.

    这篇关于WCF 本身是否支持多线程?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

  • 查看全文
    登录 关闭
    扫码关注1秒登录
    发送“验证码”获取 | 15天全站免登陆