首页 新闻 搜索 专区 学院

wcf多线程访问为何会有阻塞情况出现呢?

0
悬赏园豆:20 [已解决问题] 解决于 2015-07-24 15:58

wcf服务有个方法,每次调用都返回一个数据集合,肯定这个方法是不需要耗时的

客户端单线程调用这个方法时每次大概耗时100毫秒

如果采用多线程 例如5个线程 最快的一个线程需要200毫秒,最慢的一个线程大概400毫秒

几个线程的执行时长呈现递增的情况,且如果服务端返回的数据集合内容越大,

各个线程相互间的执行时间差越大,感觉好像网络传输会有阻塞(wcf使用nettcp连接)

另外还有个问题:如何提高wcf的网络传输性能呢,我现在服务跟客户端都在一台机子上

应该说传输是0毫秒的,时间估计都消耗在数据的转换上吧。关于这个试了很多方法,一直无法提高下性能,减少时间。

Wcf
问题补充:

经过反复的测试发现当有多个线程同时调用wcf服务时,网络传输会出现阻塞

 模拟场景是这样的

创建二十个线程,每个线程里都进行wcf连接的创建

然后只统计调用wcf方法的时长

分析发现

第18个线程

调用wcf方法的时间为:2015-07-23 18:14:51;910

Wcf服务端接收到的时间为:2015-07-23 18:14:52;446

这个过程用了将近500毫秒

如果是单线程,则这个时间基本可以忽略不计,为2毫秒

如果线程越多,这个时间会越长

当然我wcf服务是支持多线程访问的,且连接数也是足够的,不应该出现排队才对

不知道为什么会出现这种情况、

相关代码及执行情况(突然想了下我客户端是winform程序,难难道跟这个有关系??)

 [GlobalExceptionHandlerBehaviour(typeof(GlobalExceptionHandler))]
    [ServiceBehavior(
        InstanceContextMode = InstanceContextMode.PerCall,
        ConcurrencyMode = ConcurrencyMode.Multiple,//多线程
        UseSynchronizationContext = false //你需要评估的service的整个操作是否真的需要依赖于当前UI线程,如果不需要或者只有部分操作需要,将UseSynchronizationContext 设成false,将会提高service处理的并发量。对于依赖于当前UI线程的部分操作,可以通过SynchronizationContext实现将操作Marshal到UI线程中处理,对于这种操作,应该尽力那个缩短执行的时间
        )]
    [AspNetCompatibilityRequirements(RequirementsMode = AspNetCompatibilityRequirementsMode.Allowed)]//这个地方最好是 allowed ,而不是required.
    public class BaseDataServ : IBaseDataServ
    {

        public List<CarTree> GetUserPowerCarTree(int userID, string sessionID)
        {
           Common.Log.DeBugLog(sessionID + "-开始获取权限车辆树-" + userID.ToString(), sessionID);
            System.Diagnostics.Stopwatch st = new System.Diagnostics.Stopwatch();
            st.Start();

            List<CarTree> re = new List<CarTree>();
            for (int i = 0; i <= 10000; i++)
            {
                re.Add(new CarTree());
            }
            Thread.Sleep(500);
            Common.Log.DeBugLog(sessionID + "-完成获取权限车辆树-" + userID.ToString() + "耗时:" + st.ElapsedMilliseconds.ToString(), sessionID);
            return re;
        }


}
服务端代码
for (int i = 0; i < 5; i++)
            {
                listRunConfig.Add(new RunConfig
                {
                    key = "",
                   ThreadNum = i,
                    TotalNum =1
                });
            }
  listRunConfig.ForEach(p => AsynGetUserPowerCarTree(p));
 private void AsynGetUserPowerCarTree(RunConfig config)
        {
            try
            {
                var myThread = new Thread(GetUserPowerCarTree);
                myThread.IsBackground = true;
                myThread.Start(config);
            }
            catch (Exception e)
            {
               Common.Log.ErrLog("AsynGetCompany", e);
            }
        }


        private void GetUserPowerCarTree(object obj)
        {
            var config = (RunConfig)obj;

           UseService(p =>  p.GetUserPowerCarTree(config.Key, config.ThreadNum), string.Format("获取权限车辆树-{0}-{1}", config.Key, config.ThreadNum));




}


 /// <summary>
        /// 执行wcf服务方法
        /// </summary>
        /// <param name="comID">公司ID 用于辨别调用哪个wcf服务</param>
        /// <param name="action"></param>
       void UseService( Action<BaseData.BaseDataServClient> action, string souce)
        {
            
                serv = new BaseData.BaseDataServClient("NetTcpBinding_IBaseDataServ872");
                serv.Open();
            System.Diagnostics.Stopwatch st = new System.Diagnostics.Stopwatch();
            st.Start();
          Common.Log.DeBugLog("获取wcf数据-开始-" + souce, string.Format("耗时{0}毫秒", st.ElapsedMilliseconds));
            action(serv);
          Common.Log.DeBugLog("获取wcf数据-完成-" + souce, string.Format("耗时{0}毫秒", st.ElapsedMilliseconds));
 serv.Close();
        }

}
客户端代码
<services>
      <service behaviorConfiguration="OwnTCPBehavior" name="WCF.BaseData.BaseDataServ">
        <!--     address="net.tcp://127.0.0.1:872/BaseData" -->
        <endpoint   binding="netTcpBinding" bindingConfiguration="OwnTcpBinding" contract="WCF.BaseData.IBaseDataServ"   />
        <endpoint  binding="basicHttpBinding" bindingConfiguration="BasicHttpBinding_IServer"  contract="WCF.BaseData.IBaseDataServ" address="">
          <identity>
            <dns value="localhost" />
          </identity>
        </endpoint>
        <!--<endpoint  binding="mexHttpBinding" contract="IMetadataExchange" address="mex"/>-->
        <host>
          <baseAddresses>
            <add baseAddress="http://127.0.0.1:871/BaseData" />
            <add baseAddress="net.tcp://127.0.0.1:872/BaseData" />
          </baseAddresses>
        </host>
      </service>
    </services>
<behaviors>
      <serviceBehaviors>
        <behavior name="OwnTCPBehavior">
          <!-- 为避免泄漏元数据信息,
          请在部署前将以下值设置为 false -->
          <serviceMetadata httpGetEnabled="true" />
          <!-- 要接收故障异常详细信息以进行调试,
          请将以下值设置为 true。在部署前设置为 false
          以避免泄漏异常信息 -->
          <serviceDebug includeExceptionDetailInFaults="true" />
          <!--          
          使用 ServiceThrottlingBehavior 类可控制各种吞吐量设置,这些设置可以让您优化服务性能,以帮助避免应用程序内存不足。
          http://msdn.microsoft.com/zh-cn/library/system.servicemodel.description.servicethrottlingbehavior(v=vs.110).aspx
          MaxConcurrentCalls 属性可限制当前在整个 ServiceHost 中处理的消息数目。默认为处理器计数的 16 倍。 
          MaxConcurrentInstances 属性可限制在整个 ServiceHost 中一次执行的 InstanceContext 对象数。默认为 MaxConcurrentSessions 的值和 MaxConcurrentCalls 值的总和。
          MaxConcurrentSessions 属性可限制 ServiceHost 对象可以接受的会话数。服务主机可接受的最大会话数。 默认为处理器计数的 100 倍。 
          因为运行时负载平衡需要运行应用程序的经验,所以,通过应用程序配置文件使用 ServiceThrottlingBehavior 是修改执行过程以获得最佳服务性能的最常用方法。
          配置文件中使用 <serviceThrottling> 元素来设置此属性的值。
          -->
          <serviceThrottling maxConcurrentCalls="100"  maxConcurrentSessions="100" maxConcurrentInstances="200"/>
          <dataContractSerializer maxItemsInObjectGraph="6553600" />
        </behavior>
      </serviceBehaviors>
      <endpointBehaviors>
        <behavior name="NewBehavior">
          <dataContractSerializer maxItemsInObjectGraph="6553600" />
        </behavior>
      </endpointBehaviors>
    </behaviors>
<netTcpBinding>

        <binding name="OwnTcpBinding"
                  closeTimeout="00:01:03"
                  openTimeout="00:01:04"
                  receiveTimeout="00:05:01"
                  sendTimeout="00:05:02"
                 transactionFlow="false"
                transferMode="Buffered"
                 transactionProtocol="OleTransactions"
                  hostNameComparisonMode="StrongWildcard"
                 maxBufferPoolSize="524288"
                 
                 maxBufferSize="6553600"
                 maxReceivedMessageSize="6553600">

          <!--定义可由采用此绑定配置的终结点进行处理的 SOAP 消息的复杂性约束
maxArrayLength:一个正整数,指定所允许的最大数组长度。默认值为 16384。
maxBytesPerRead:一个正整数,指定每项读取操作返回的所允许的最大字节数。默认值为 4096。
maxDepth:一个正整数,指定每项读取操作的最大嵌套节点深度。默认值为 32。
maxNameTableCharCount:一个正整数,指定表名称中允许的最大字符数。默认值为 16384。
maxStringContentLength:一个正整数,指定 XML 元素内容中允许包含的最大字符数。默认值为 8192-->
          <readerQuotas maxDepth="32"
                        maxStringContentLength="8192"
                        maxArrayLength="16384"
                        maxBytesPerRead="4096"
                        maxNameTableCharCount="16384" />
          <!--指定是否在通道终结点之间建立可靠会话。
inactivityTimeOut:一个 TimeSpan,指定通道在出错之前允许其他通信方不发送任何消息的最大持续时间。
                   通道上的活动被定义为接收应用程序或基础结构消息。如果在此属性指定的时间内未检测到活动,则基础结构会中止会话,且通道会出错。可靠会话被中止。
                  默认值为 00:10:00。
Ordered:一个布尔值,指定是否保证消息以其发送顺序抵达。默认值为 true。-->

          <reliableSession ordered="true"
                           inactivityTimeout="00:10:00"
                           enabled="false" />
          <!--定义绑定的安全设置
          <security mode="None">
            <transport clientCredentialType="Windows" protectionLevel="EncryptAndSign" />
          </security>-->
        </binding>

      </netTcpBinding>
WCF服务端配置
客户端--获取wcf数据-开始--线程0  2015-07-24 08:50:03;324

客户端--获取wcf数据-开始--线程2  2015-07-24 08:50:03;326

客户端--获取wcf数据-开始--线程1   2015-07-24 08:50:03;331

客户端--获取wcf数据-开始--线程4   2015-07-24 08:50:03;334

客户端--获取wcf数据-开始--线程3   2015-07-24 08:50:03;336


客户端--获取wcf数据-完成--线程0   2015-07-24 08:50:04;027   耗时701毫秒

客户端--获取wcf数据-完成--线程1   2015-07-24 08:50:04;039   耗时706毫秒

客户端--获取wcf数据-完成--线程2   2015-07-24 08:50:04;046   耗时718毫秒

客户端--获取wcf数据-完成--线程4   2015-07-24 08:50:04;429   耗时1094毫秒

客户端--获取wcf数据-完成--线程3   2015-07-24 08:50:04;523   耗时1191毫秒
客户端执行日志
服务端-接收到客户端请求-线程0   2015-07-24 08:50:03;328

服务端-接收到客户端请求-线程2   2015-07-24 08:50:03;332

服务端-接收到客户端请求-线程1   2015-07-24 08:50:03;347

服务端-接收到客户端请求-线程4   2015-07-24 08:50:03;815


服务端-返回到客户端请求-线程0  2015-07-24 08:50:03;831  耗时:501

服务端-返回到客户端请求-线程2  2015-07-24 08:50:03;837  耗时:502

服务端-返回到客户端请求-线程1  2015-07-24 08:50:03;851  耗时:502

服务端-接收到客户端请求-线程3  2015-07-24 08:50:03;903

服务端-返回到客户端请求-线程4  2015-07-24 08:50:04;322  耗时:506 

服务端-返回到客户端请求-线程3  2015-07-24 08:50:04;408  耗时:503
服务端执行日志
按时间先后顺序合并日志
thomaschen的主页 thomaschen | 初学一级 | 园豆:7
提问于:2015-07-23 12:12
< >
分享
最佳答案
0

Stopwatch的使用有问题,在 st.Start(); 之前要进行reset—— st.Reset(); 

收获园豆:20
dudu | 高人七级 |园豆:36516 | 2015-07-24 14:34
其他回答(1)
0

你是不是对返回数据的方法做单例或者lock了? 理论上不可能的把

万雅虎 | 园豆:318 (菜鸟二级) | 2015-07-23 17:05

没有,wcf服务端直接返回数据,没有做任何处理的

理论上来说 单个线程执行时长100毫秒

N个线程的话应该每个线程也接近100毫秒才对

现在是每个线程执行时长都是递增的

支持(0) 反对(0) thomaschen | 园豆:7 (初学一级) | 2015-07-23 17:59
清除回答草稿
   您需要登录以后才能回答,未注册用户请先注册