Wcf并发处理的例子


勇哥注:

同一个客户端的多次会话,会建立不同的线程。

多个客户端的访问也是如此。


理解WCF中的并发机制

 

  • 在对WCF并发机制进行理解时,必须对WCF中的实例化进行理解,因为WCF中的并发特点是伴随着服务实例上下文实现的。WCF的实例上下文模型可以通过InstanceContext的属性来进行设置,WCF中的并发就是指一个实例上下文处理请求消息的能力,当需要在一个实例上下文中处理多个消息请求时就会产生并发。所以当InstanceContextMode的值为PerSession或Single的时候就会产生并发的情况,这时我们可以通过设置ConcurrencyMode的值来控制服务并发处理消息的模式。

  • ConcurrencyMode指定服务类是支持单线程还是多线程操作模式。具有以下三个值:

  1. Single:服务实例是单线程的,且不接受可重入调用。如果 InstanceContextMode 属性为 Single,且其他消息在实例处理调用的同时到达,则这些消息必须等待,直到服务可用或消息超时为止。

  2. Reentrant:服务实例是单线程的,且接受可重入调用。可重入服务接受在调用其他服务的同时进行调用;因此在调出之前,您需要负责让对象的状态一致,而在调出之后,必须确认本地操作数据有效。请注意,只有通过通道调用其他服务,才能解除服务实例锁定。在此情况下,已调用的服务可以通过回调重入第一个服务。如果第一个服务不可重入,则该调用顺序会导致死锁。

  3. Multiple:服务实例是多线程的。无同步保证。因为其他线程可以随时更改服务对象,所以必须始终处理同步与状态一致性。

 

理解并发模式与实例上下文模式的关系

 

  • 当我们的InstanceContextMode设置为PerSession时,一个客户端会话模型就会产生一个服务实例上下文,此时如果我们的ConcurrencyMode值设置为Single,那么服务将以串行的模式进行消息处理,因为并发模式采用的是单线程模式,所以一次只会处理一个消息,并且同一个实例上下文模型中的线程ID一样。

  • 当我们把InstanceContextMode设置为PerSession,ConcurrencyMode值设置为Multiple时,服务采用多线程处理模型。即一个实例上下文中会出现多个线程来处理消息请求,这样就大大加快程序处理的能力,但不是绝对的。程序的处理能力加大就会对服务器产生消耗,所以在对消息进行并发处理时,我们也要对处理的最大能力进行限制。而已采用多线程模型处理消息时,一定要保证线程时安全的。(这一部分需要各位多线程进行友好的理解)

  • 什么情况下我们才会遇到ConcurrencyMode为Reentrant的情况呢?Single采用的是单线程处理模型,当客户端调用服务端方法时,就会给方法加上锁,如果此时服务端需对客户端产生回调,并且回调的方法采用的是请求\应答的消息模型,当服务对客户端调用完成后,就会尝试重新获取实例上下文模型对接下来的程序逻辑进行处理,并且会对实例上下文进行加锁,但是此时的实例上下文在之前已经被锁住了。回调之前的实例上下文只有在消息处理完成后才能释放锁,而回调后的实例上下文又不能获得锁,导致操作永远无法完成,这就产生了死锁。此时就可以将ConcurrencyMode设置为Reentrant解决此问题。(也可以将ConcurrencyMode设置为Multiple解决此问题,因为设置为Multiple后采用的是多线程处理模型)




下面是工程结构: 

image.png



注意下面的线程ID的不同。

image.png


winform的代码:

using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Forms;

namespace WindowsFormsApp1
{
    public partial class Form1 : Form
    {
        private client.SampleMethodClient myclinet1 = new client.SampleMethodClient();
        private client.SampleMethodClient myclinet2 = new client.SampleMethodClient();
        public Form1()
        {
            InitializeComponent();
        }

        private void button1_Click(object sender, EventArgs e)
        {
            Task.Factory.StartNew(() =>
            {
                myclinet1.MethodOneAsync(new client.MethodOneRequest("c1_1"));
                myclinet1.MethodOneCompleted += Myclinet1_MethodOneCompleted;

                myclinet1.MethodTwoAsync(new client.MethodTwoRequest("c1_2"));
                myclinet1.MethodTwoCompleted += Myclinet1_MethodTwoCompleted;

                myclinet2.MethodOneAsync(new client.MethodOneRequest("c2_1"));
                myclinet2.MethodOneCompleted += Myclinet2_MethodOneCompleted;

                myclinet2.MethodTwoAsync(new client.MethodTwoRequest("c2_2"));
                myclinet2.MethodTwoCompleted += Myclinet2_MethodTwoCompleted;
            });
           
        }

        private void Myclinet2_MethodTwoCompleted(object sender, client.MethodTwoCompletedEventArgs e)
        {
            OutMsg(e.Result.MethodTwoResult.ToString());
        }

        private void Myclinet2_MethodOneCompleted(object sender, client.MethodOneCompletedEventArgs e)
        {
            OutMsg(e.Result.MethodOneResult.ToString());
        }

        private void Myclinet1_MethodTwoCompleted(object sender, client.MethodTwoCompletedEventArgs e)
        {
            OutMsg(e.Result.MethodTwoResult.ToString());
        }

        private void Myclinet1_MethodOneCompleted(object sender, client.MethodOneCompletedEventArgs e)
        {
            OutMsg(e.Result.MethodOneResult.ToString());
        }

        public void OutMsg(string str)
        {
            if(RtbMsg.InvokeRequired)
            {
                Action<string> act = OutMsg;
                RtbMsg.Invoke(act, new object[] { str  });
            }
            else
            {
                RtbMsg.AppendText(str+"\n");
            }
        }
    }
}

Host的配置:

<?xml version="1.0" encoding="utf-8" ?>
<configuration>
    <startup> 
        <supportedRuntime version="v4.0" sku=".NETFramework,Version=v4.6.1" />
    </startup>
  <system.serviceModel>
    <behaviors>
      <serviceBehaviors>
        <behavior name="SampleMethodbehavior">
          <serviceDebug httpHelpPageEnabled="false"/>
          <serviceMetadata httpGetEnabled="false"/>
          <serviceTimeouts transactionTimeout="00:10:00"/>
          <serviceThrottling maxConcurrentCalls="1000" maxConcurrentInstances="1000" maxConcurrentSessions="1000"/>
        </behavior>
      </serviceBehaviors>
    </behaviors>
    <bindings>
      <netTcpBinding>
        <binding name="tcpbinding">
          <security mode="None">
            <transport clientCredentialType="None" protectionLevel="None"/>
          </security>
        </binding>
      </netTcpBinding>
    </bindings>
    <services>
      <service name="ClassLibrary1.SampleMethod" behaviorConfiguration="SampleMethodbehavior">
        <host>
          <baseAddresses>
            <add baseAddress="net.tcp://localhost:11112/SampleMethod"/>
          </baseAddresses>
        </host>
        <endpoint address="" binding="netTcpBinding" bindingConfiguration="tcpbinding" contract="ClassLibrary1.ISampleMethod"/>
        <endpoint address="mex" binding="mexTcpBinding" contract="IMetadataExchange"/>
      </service>

    </services>
  </system.serviceModel>
</configuration>


SampleMethod.cs

using System;
using System.Collections.Generic;
using System.Linq;
using System.ServiceModel;
using System.Text;
using System.Threading;
using System.Threading.Tasks;

namespace ClassLibrary1
{
    [ServiceBehavior(InstanceContextMode = InstanceContextMode.PerSession, ConcurrencyMode = ConcurrencyMode.Multiple)]
    public class SampleMethod : ISampleMethod
    {

        public string MethodOne(string msg)
        {
            OperationContext operationContext = OperationContext.Current;
            InstanceContext instanceContext = operationContext.InstanceContext;
            string info = "InstanceContext HashCode: " + instanceContext.GetHashCode().ToString();
            System.Threading.Thread.Sleep(5000);
            return msg + "\n->" + info + "\n->ManagedThreadId:" + Thread.CurrentThread.ManagedThreadId.ToString() + "  " + System.DateTime.Now.ToString();
        }

        public string MethodTwo(string msg)
        {
            OperationContext operationContext = OperationContext.Current;
            InstanceContext instanceContext = operationContext.InstanceContext;
            string info = "InstanceContext HashCode: " + instanceContext.GetHashCode().ToString();
            System.Threading.Thread.Sleep(2000);
            return  msg + "\n->" + info + "\n->ManagedThreadId:" + Thread.CurrentThread.ManagedThreadId.ToString() + "  " + System.DateTime.Now.ToString();
        }

    }
}


接口:

using System;
using System.Collections.Generic;
using System.Linq;
using System.ServiceModel;
using System.Text;
using System.Threading.Tasks;

namespace ClassLibrary1
{

    [ServiceContract(SessionMode = SessionMode.Required)]
    public interface ISampleMethod
    {
        [OperationContract]
        string MethodOne(string msg);

        [OperationContract]
        string MethodTwo(string msg);
    }
}


本文出自勇哥的网站《少有人走的路》wwww.skcircle.com,转载请注明出处!讨论可扫码加群:
本帖最后由 勇哥,很想停止 于 2024-04-03 17:08:22 编辑

发表评论:

◎欢迎参与讨论,请在这里发表您的看法、交流您的观点。

会员中心
搜索
«    2025年4月    »
123456
78910111213
14151617181920
21222324252627
282930
网站分类
标签列表
最新留言
    热门文章 | 热评文章 | 随机文章
文章归档
友情链接
  • 订阅本站的 RSS 2.0 新闻聚合
  • 扫描加本站机器视觉QQ群,验证答案为:halcon勇哥的机器视觉
  • 点击查阅微信群二维码
  • 扫描加勇哥的非标自动化群,验证答案:C#/C++/VB勇哥的非标自动化群
  • 扫描加站长微信:站长微信:abc496103864
  • 扫描加站长QQ:
  • 扫描赞赏本站:
  • 留言板:

Powered By Z-BlogPHP 1.7.2

Copyright Your skcircle.com Rights Reserved.

鄂ICP备18008319号


站长QQ:496103864 微信:abc496103864