【问题标题】:Self-hosted WCF service: ConcurrencyMode.Multiple but only using one thread?自托管 WCF 服务:ConcurrencyMode.Multiple 但只使用一个线程?
【发布时间】:2012-07-29 04:08:55
【问题描述】:

我已经迈出了使用 WCF 开发 WINDOWS 服务托管计算引擎的第一步。该服务运行良好,但它似乎只为我的所有调用使用一个线程,但我需要它具有可扩展性并使用多线程。

忽略代码等中的所有跟踪,因为这是此应用程序的早期阶段。

它调用一些第三方软件 (COM),在服务启动时将信息读入内存 (ThirdParty.Initialise(strInit)),然后每次调用 ProcessInformation 从给定的 XML 字符串返回计算结果。

到目前为止,无论我将 ServiceBehavior atts 设置为什么,也无论我使用多少单独的消费者,所有调用似乎都只使用一个线程 - 有人可以帮忙吗?

代码概览:

[ServiceBehavior(ConcurrencyMode = ConcurrencyMode.Multiple, InstanceContextMode = InstanceContextMode.PerCall, UseSynchronizationContext = false)]
public class TestWCFService : ITestWCFService
{
    private static ThirdPartyLib.ThirdPartyComClass ThirdParty;

    private static bool initCalled = false;
    private static int cntInit = 0;
    private static int cntTrans = 0;

    public TestWCFService()
    {
        if (ThirdParty == null)
        {
            ThirdParty = new ThirdPartyLib.ThirdPartyComClass();
        }
    }

    public bool InitialiseThirdParty(string strInit, out string strError)
    {
        try
        {
            if (!initCalled)
            {
                cntInit++;
                ThirdParty.Initialise(strInit);
                initCalled = true;
            }
            strError = "Call Num " + cntInit;
            return true;
        }
        catch (Exception ex)
        {
            strError = "ThirdParty.Initialise exception " + ex.Message + " 0n call number " + cntInit;
            return false;
        }
    }

    public bool ProcessInformation(string strRequestXML, int quoteMarker, out string strResponseXML, out string strError, out int quoteMarkerReturned)
    {
        try
        {
            cntTrans++;
            quoteMarkerReturned = quoteMarker;
            ThirdParty.ProcessInformation(strRequestXML, out strResponseXML);
            strError = "Call Trans Num " + cntTrans;
            return true;
        }
        catch (Exception ex)
        {
            strError = ex.Message + " On call trans num " + cntTrans;
            strResponseXML = "Error";
            quoteMarkerReturned = quoteMarker;
            return false;
        }
    }
}

配置:

<?xml version="1.0" encoding="utf-8" ?>
<configuration>
  <system.web>
    <compilation debug="true" />
  </system.web>
  <system.serviceModel>
    <bindings>
      <wsHttpBinding>
        <binding name="TestServiceBinding" bypassProxyOnLocal="true"
          useDefaultWebProxy="false">
          <readerQuotas maxDepth="524288" maxStringContentLength="524288"
            maxArrayLength="524288" maxBytesPerRead="524288" maxNameTableCharCount="524288" />
          <reliableSession inactivityTimeout="00:30:00" enabled="true" />
        </binding>
      </wsHttpBinding>
      <mexHttpBinding>
        <binding name="MEXTestServiceBinding" openTimeout="00:02:00"
          sendTimeout="00:02:00" />
      </mexHttpBinding>
    </bindings>
    <services>
      <service behaviorConfiguration="TestWcfServiceLibrary.TestWCFServiceBehavior"
        name="TestWcfServiceLibrary.TestWCFService">
        <endpoint address="" binding="wsHttpBinding" bindingConfiguration="TestServiceBinding"
          name="" contract="TestWcfServiceLibrary.ITestWCFService">
          <identity>
            <dns value="localhost" />
          </identity>
        </endpoint>
        <endpoint address="mex" binding="mexHttpBinding" bindingConfiguration="MEXTestServiceBinding"
          contract="IMetadataExchange" />
        <host>
          <baseAddresses>
            <add baseAddress="http://localhost:8731/Design_Time_Addresses/TestWcfServiceLibrary/TestWCFService/" />
          </baseAddresses>
        </host>
      </service>
    </services>
    <behaviors>
      <serviceBehaviors>
        <behavior name="TestWcfServiceLibrary.TestWCFServiceBehavior">
          <serviceMetadata httpGetEnabled="True"/>
          <serviceThrottling maxConcurrentCalls="100" maxConcurrentSessions="100" maxConcurrentInstances="100"/>
          <serviceDebug includeExceptionDetailInFaults="False" />
        </behavior>
      </serviceBehaviors>
    </behaviors>
  </system.serviceModel>
</configuration>

【问题讨论】:

  • 你怎么知道只使用了一个线程?我猜您的第三方 COM 组件是单元线程的,因此具有线程亲和性,因此无论服务实现使用多少 .NET 线程,它们都必须排队才能使用唯一的特定 STA 线程能够调用 COM 对象。

标签: c# .net multithreading wcf self-hosting


【解决方案1】:

您的第三方 COM 组件似乎很可能是单元线程的,因此具有线程关联性。如果是这样,那么在 COM 组件中将只有一个线程在做这项工作。

无论服务实现使用多少 .NET 线程,它们都必须排队使用特定的 STA 线程,这是唯一能够调用 COM 对象的线程。

【讨论】:

  • 确实正确。我被引导相信它是多线程的,但通过运行替换函数,它现在 100% 是第三方问题。无论如何,感谢所有快速回复。看起来可能需要一些花哨的步法来利用一些额外的核心:(。
  • 如果可以的话,分享花哨的步法......学习一两件事永远不会太晚。
【解决方案2】:

根据 Microsoft 文档:

并发的使用与实例化模式有关。在 PerCall 中 实例化,并发是不相关的,因为每条消息都是 由新的服务实例处理。

// Multiple allows concurrent processing of multiple messages by a service instance.
// The service implementation should be thread-safe. This can be used to increase throughput.
[ServiceBehavior(ConcurrencyMode=ConcurrencyMode.Multiple, InstanceContextMode = InstanceContextMode.Single)]

// Uses Thread.Sleep to vary the execution time of each operation.
public class CalculatorService : ICalculatorConcurrency
{
    int operationCount;

    public double Add(double n1, double n2)
    {
        operationCount++;
        System.Threading.Thread.Sleep(180);
        return n1 + n2;
    }

    public double Subtract(double n1, double n2)
    {
        operationCount++;
        System.Threading.Thread.Sleep(100);
        return n1 - n2;
    }

    public double Multiply(double n1, double n2)
    {
        operationCount++;
        System.Threading.Thread.Sleep(150);
        return n1 * n2;
    }

    public double Divide(double n1, double n2)
    {
        operationCount++;
        System.Threading.Thread.Sleep(120);
        return n1 / n2;
    }

    public string GetConcurrencyMode()
    {   
        // Return the ConcurrencyMode of the service.
        ServiceHost host = (ServiceHost)OperationContext.Current.Host;
        ServiceBehaviorAttribute behavior = host.Description.Behaviors.Find<ServiceBehaviorAttribute>();
        return behavior.ConcurrencyMode.ToString();
    }

    public int GetOperationCount()
    {   
        // Return the number of operations.
        return operationCount;
    }
}

示例输出:([]中的数字表示线程id)

2012-07-31 09:07:28,509 [9] DEBUG Microsoft.ServiceModel.Samples.CalculatorService - Leaving: Microsoft.ServiceModel.Samples.CalculatorService.Multiply(double, double) : 752.919376325402
2012-07-31 09:07:28,512 [17] DEBUG Microsoft.ServiceModel.Samples.CalculatorService - Leaving: Microsoft.ServiceModel.Samples.CalculatorService.Multiply(double, double) : 752.919376325402
2012-07-31 09:07:28,524 [13] DEBUG Microsoft.ServiceModel.Samples.CalculatorService - Leaving: Microsoft.ServiceModel.Samples.CalculatorService.Multiply(double, double) : 2143.10173334651
2012-07-31 09:07:28,524 [11] DEBUG Microsoft.ServiceModel.Samples.CalculatorService - Entering: Microsoft.ServiceModel.Samples.CalculatorService.Multiply(double n1 = 15.5665354410031, double n2 = 48.3678194919451)
2012-07-31 09:07:28,532 [22] DEBUG Microsoft.ServiceModel.Samples.CalculatorService - Entering: Microsoft.ServiceModel.Samples.CalculatorService.Multiply(double n1 = 94.7194438868758, double n2 = 29.8120223590229)
2012-07-31 09:07:28,534 [9] DEBUG Microsoft.ServiceModel.Samples.CalculatorService - Entering: Microsoft.ServiceModel.Samples.CalculatorService.Multiply(double n1 = 99.2045067247024, double n2 = 88.4957458770349)
2012-07-31 09:07:28,539 [4] DEBUG Microsoft.ServiceModel.Samples.CalculatorService - Entering: Microsoft.ServiceModel.Samples.CalculatorService.Multiply(double n1 = 99.2045067247024, double n2 = 88.4957458770349)
2012-07-31 09:07:28,539 [7] DEBUG Microsoft.ServiceModel.Samples.CalculatorService - Entering: Microsoft.ServiceModel.Samples.CalculatorService.Multiply(double n1 = 37.991849630136, double n2 = 41.7864370820049)
2012-07-31 09:07:28,539 [17] DEBUG Microsoft.ServiceModel.Samples.CalculatorService - Entering: Microsoft.ServiceModel.Samples.CalculatorService.Multiply(double n1 = 11.331077670367, double n2 = 55.5888338273339)
2012-07-31 09:07:28,539 [11] DEBUG Microsoft.ServiceModel.Samples.CalculatorService - Leaving: Microsoft.ServiceModel.Samples.CalculatorService.Multiply(double, double) : 752.919376325402
2012-07-31 09:07:28,539 [22] DEBUG Microsoft.ServiceModel.Samples.CalculatorService - Leaving: Microsoft.ServiceModel.Samples.CalculatorService.Multiply(double, double) : 2823.77817898976
2012-07-31 09:07:28,539 [17] DEBUG Microsoft.ServiceModel.Samples.CalculatorService - Leaving: Microsoft.ServiceModel.Samples.CalculatorService.Multiply(double, double) : 629.881393702645
2012-07-31 09:07:28,542 [9] DEBUG Microsoft.ServiceModel.Samples.CalculatorService - Leaving: Microsoft.ServiceModel.Samples.CalculatorService.Multiply(double, double) : 8779.17681696586
2012-07-31 09:07:28,544 [4] DEBUG Microsoft.ServiceModel.Samples.CalculatorService - Leaving: Microsoft.ServiceModel.Samples.CalculatorService.Multiply(double, double) : 8779.17681696586
2012-07-31 09:07:28,544 [7] DEBUG Microsoft.ServiceModel.Samples.CalculatorService - Leaving: Microsoft.ServiceModel.Samples.CalculatorService.Multiply(double, double) : 1587.54403419867

【讨论】:

  • 我真的希望事情就这么简单。我已经尝试了这些 atts 的所有配置,但似乎没有任何区别。只使用一个线程。
  • 我不确定它是否使用了仅使用一个线程的第三方 DLL。
  • 好吧,您的服务一开始就不是线程安全的。您在 InitialiseThirdParty 中有一个竞速条件。如果可以生成多个线程,我将测试示例代码。你的测试方法是什么?你怎么知道你只有一个线程?
  • 服务本身在启动时只对 Initialise 进行了一次调用,因此没有竞争条件等。我正在使用 Process Explorer 显示线程,然后堆栈跟踪等,主要保留在第三方 DLL 中是完成所有工作的地方。不用担心它只是一个测试平台的代码 - 我已经编写高性能多线程应用程序 20 多年了,所以请忽略糟糕的编码,因为它只是一个测试平台哈哈。
  • 您可能是对的,问题出在第 3 方 DLL 上。我已经使用上面的示例进行了测试,它似乎为我生成了多个线程(使用 PostSharp 和 Log4Net 进行跟踪,观看 Process Explorer)。
【解决方案3】:

问题在于第三方 DLL 只是一个单线程 COM 模型。我被引导相信它是多线程的,但通过运行替换功能,它现在 100% 是第三方问题。无论如何,感谢所有快速回复。看起来可能需要一些花哨的步法来利用一些额外的核心。我已经向公司发送了一封电子邮件,看看他们是否有 MT 版本,但我对此表示怀疑。再次感谢。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2018-08-25
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2013-07-10
    相关资源
    最近更新 更多