Поддерживает ли WCF многопоточность? - PullRequest
10 голосов
/ 21 июля 2010

Я разработал концептуальное приложение, которое запрашивает, поддерживает ли WCF многопоточность.

Теперь все, что я сделал, - это создание контракта на обслуживание, помеченного

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

с двумя операциями.чтобы получить фиксированные тексты.Первый метод делает Thread.Sleep в течение 8 секунд, чтобы задержать ответ, а другой возвращает данные напрямую.

Проблема, с которой я столкнулся, была, когда я запускаю два экземпляра клиентского приложения и запрашиваю у первого клиентаметод с задержкой и запрос другого метода от второго клиента, я получил последовательный ответ.

Как я могу получить ответ от службы, когда служба занята другим запросом?

 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
    }
}

Мне нужно вызвать методы GetDelayedResponse и GetDirectResponse одновременно и получить «быстрый» текст до истечения 8 секунд.


Код приложения хостинга

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

Ниже приведен код, который я реализую для проверки кейса:

Классы на стороне сервера,

  1. Интерфейс службы

    namespace WCFSessionServer
    {
        [ServiceContract]
        public interface IService
    
        {
            [OperationContract]
            string GetDelayedResponse();
    
           [OperationContract]
           string GetDirectResponse();
        }
     }
    
  2. Класс реализации

    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
         }
     }
    
  3. Серверное приложение 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>
    

Клиентское приложение.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>

Ответы [ 2 ]

17 голосов
/ 21 июля 2010

Ну, определив ваш сервис как

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

Вы в основном определяете класс обслуживания как одиночный (InstanceContextMode.Single), что, безусловно, не лучший подход. Определяя его как ConcurrencyMode.Multiple, вы делаете его многопоточным синглтоном, что накладывает большую нагрузку на то, чтобы ваш код был на 200% поточно-ориентированным на ваши плечи.

Моя рекомендация - пометить класс реализации вашего сервиса как per-call.

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

При таком подходе среда выполнения WCF сама раскручивает столько классов экземпляров служб, сколько необходимо для обработки ваших запросов. В вашем примере среда выполнения WCF создаст и запустит два экземпляра ServiceImplementation, по одному для каждого запроса, и будет обрабатывать вызовы одновременно. Большим преимуществом является то, что, поскольку каждый класс экземпляра службы обслуживает только один запрос, вам не нужно беспокоиться об управлении параллелизмом в своем коде - вы находитесь внутри «однопоточного» класса, и среда выполнения WCF решает все вопросы, связанные с иметь несколько запросов и обрабатывать их соответствующим образом.

Обновление: вы все еще не , показывающий, как вы создаете свой прокси-сервер службы на стороне клиента, и как вы вызываете свою службу. Вы опубликовали практически весь код на стороне сервера, но не код на стороне клиента.

ОК, вот как это сделать:

  • раскрутить хост службы и убедиться, что он работает
  • в Visual Studio создайте два отдельных проекта консольных приложений для своих клиентов - назовите их Client1 и Client2
  • в обоих этих новых клиентских проектах используйте Add Service Reference, чтобы добавить ссылку на службу к вашей службе
  • , который создаст группу файлов под глобусом "Service Reference"

  • Теперь вам нужно создать экземпляр клиентского прокси в обоих ваших клиентских проектах:

     In Client1:
         var instance1 = new ServiceImplementationClient();
    
     In Client2:
         var instance2 = new ServiceImplementationClient();
    
  • Client1 вызовет ваш первый метод GetDelayedResponse, а Client2 вызовет GetDirectResponse:

     In Client1:
         instance1.GetDelayedResponse();
    
     In Client2:
         instance2.GetDirectResponse();
    
  • если вы одновременно запустите эти два приложения, вы должны увидеть, что Client2 возвращается сразу, а Client1 будет ждать эти 8 секунд.

Если у вас есть два совершенно разных клиента, и они получат совершенно отдельный экземпляр службы на сервере, они полностью независимы друг от друга и не будут сериализовать свои вызовы и не будут блокировать друг друга.

0 голосов
/ 28 января 2019

Попробуйте изменить значение атрибута

UseSynchronizationContext=false 
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...