理解WCF中的并发机制

  • 在对WCF并发机制进行理解时,必须对WCF初探-27: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后采用的是多线程处理模型)

WCF中的并发模型示例

  注意:以后此系列博文如果无特别说明,解决方案都按之前的Client、Host、Service方式进行建立,客户端代理类采用工具Scvutil.exe生成,如果不清楚可以参考此系列之前的博文。

  • 此示例采用InstanceContextMode = PerSession, ConcurrencyMode = Single的组合模型,即一个会话产生一个实例上下文,一个实例上下文只有一个线程处理请求。我们通过输出处理请求的实例上下文和线程ID就可以说明此模型的处理情形。参考代码如下:
   using System.ServiceModel;
using System.Threading; namespace Service
{
[ServiceBehavior(InstanceContextMode = InstanceContextMode.PerSession, ConcurrencyMode = ConcurrencyMode.Single)]
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();
return "You called MethodOne return message is: " + 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();
return "You called MethodTwo return message is: " + msg + "\n->" + info + "\n->ManagedThreadId:" +
              Thread.CurrentThread.ManagedThreadId.ToString() + " " + System.DateTime.Now.ToString();
} }
}

   客户端采用异步方式进行处理,所以Svcutil.exe必须生成异步客户端(svcutil.exe /out:f:\SampleMethodClient.cs /config:f:\App.confighttp://localhost:1234/SampleMethod /a /tcv:Version35),客户端参考代码如下: 

    using System;
namespace Client
{
class Program
{
static void Main(string[] args)
{
SampleMethodClient client1 = new SampleMethodClient();
client1.MethodOneCompleted += new EventHandler<MethodOneCompletedEventArgs>(client1_MethodOneCompleted);
client1.MethodOneAsync("Client1 called MethodOne");
client1.MethodTwoCompleted += new EventHandler<MethodTwoCompletedEventArgs>(client1_MethodTwoCompleted);
client1.MethodTwoAsync("Client1 called MethodTwo"); SampleMethodClient client2 = new SampleMethodClient();
client2.MethodOneCompleted += new EventHandler<MethodOneCompletedEventArgs>(client2_MethodOneCompleted);
client2.MethodOneAsync("Client2 called MethodOne");
client2.MethodTwoCompleted += new EventHandler<MethodTwoCompletedEventArgs>(client2_MethodTwoCompleted);
client2.MethodTwoAsync("Client2 called MethodTwo"); Console.Read();
} static void client2_MethodOneCompleted(object sender, MethodOneCompletedEventArgs e)
{
Console.WriteLine(e.Result.ToString());
} static void client2_MethodTwoCompleted(object sender, MethodTwoCompletedEventArgs e)
{
Console.WriteLine(e.Result.ToString());
} static void client1_MethodOneCompleted(object sender, MethodOneCompletedEventArgs e)
{
Console.WriteLine(e.Result.ToString());
}
static void client1_MethodTwoCompleted(object sender, MethodTwoCompletedEventArgs e)
{
Console.WriteLine(e.Result.ToString());
}
}
}

  运行结果:

  

  结果说明:

  Client1处理MethodOne和MethodTwo的时间点不同,但是处理的实例上下文ID和线程ID是一致的,这验证了上面的组合处理模型,并且由于客户端产生了两个代理,

  建立了两个会话,所以Client1和Client2的实例上下文ID不一致。

  • 接下来,我们将示例采用InstanceContextMode = PerSession, ConcurrencyMode = Multiple的组合模型,让服务采用多线程并发模式处理,代码只需要将 ConcurrencyMode的值改为Multiple,其他的不变,重新编译运行程序。

运行结果:

  

 

  结果说明:

  Client1处理MethodOne和MethodTwo的时间点不同,虽然处理的实例上下文ID一致,但线程ID是不一致的,这验证了上面的组合处理模型,并且由于客户端产生了两个代理,

  建立了两个会话,所以Client1和Client2的实例上下文ID不一致。

WCF初探-28:WCF中的并发的更多相关文章

  1. WCF初探文章列表

    WCF初探-1:认识WCF WCF初探-6:WCF服务配置 WCF初探-2:手动实现WCF程序 WCF初探-7:WCF服务配置工具使用 WCF初探-3:WCF消息交换模式之单向模式 WCF初探-8:W ...

  2. WCF初探-27:WCF中的实例化

    理解WCF中的实例化机制 “实例化”是指对用户定义的服务对象以及与其相关的 InstanceContext 对象的生存期的控制.也就是说我们的客户端程序在调用服务端方法时,需要实例化一个服务端代理类对 ...

  3. WCF初探-26:WCF中的会话

    理解WCF中的会话机制 在WCF应用程序中,会话将一组消息相互关联,从而形成对话.会话”是在两个终结点之间发送的所有消息的一种相互关系.当某个服务协定指定它需要会话时,该协定会指定所有调用(即,支持调 ...

  4. WCF初探-22:WCF中使用Message类(上)

    前言 从我们学习WCF以来,就一直强调WCF是基于消息的通信机制.但是由于WCF给我们做了高级封装,以至于我们在使用WCF的时候很少了解到消息的内部机制.由于WCF的架构的可扩展性,针对一些特殊情况, ...

  5. WCF初探-25:WCF中使用XmlSerializer类

    前言 在上一篇WCF序列化和反序列化中,文章介绍了WCF序列化和反序列化的机制,虽然WCF针对序列化提供了默认的DataContractSerializer序列化引擎,但是WCF还支持其他的序列化引擎 ...

  6. WCF初探-2:手动实现WCF程序

    1.前言 上一篇,我们通过VS自带的模板引擎自动生成了一个wcf程序,接下来我们将手动实现一个wcf程序.由于应用程序开发中一般都会涉及到大量的增删改查业务,所以这个程序将简单演示如何在wcf中构建简 ...

  7. WCF初探-9:WCF服务承载 (下)

    在WCF初探-8:WCF服务承载 (上)中,我们对宿主的概念.环境.特点做了文字性的介绍和概括,接下来我们将通过实例对这几种寄宿方式进行介绍.为了更好的说明各寄宿环境特点,本实例采用Http和net. ...

  8. WCF初探-10:WCF客户端调用服务

    创建WCF 服务客户端应用程序需要执行下列步骤: 获取服务终结点的服务协定.绑定以及地址信息 使用该信息创建 WCF 客户端 调用操作 关闭该 WCF 客户端对象 WCF客户端调用服务存在以下特点: ...

  9. WCF初探-11:WCF客户端异步调用服务

    前言: 在上一篇WCF初探-10:WCF客户端调用服务 中,我详细介绍了WCF客户端调用服务的方法,但是,这些操作都是同步进行的.有时我们需要长时间处理应用程序并得到返回结果,但又不想影响程序后面代码 ...

随机推荐

  1. Docker 学习笔记(CentOS 7.1)

    基本概念 Docker 包括三个基本概念 镜像(Image) 容器(Container) 仓库(Repository)理解了这三个概念,就理解了 Docker 的整个生命周期. Docker 镜像 D ...

  2. 教你如何查看一款App里面所包含的图片

    在开发制作App的过程中,有时候会想看看一些精美的App里面所设计的素材.这个时候就需要用到我给大家展现的方法了.下面就看看该如何操作能让一个App呈现出它原始的一面,这次我以Any.Do为例给大家演 ...

  3. WPF的路由事件、冒泡事件、隧道事件(预览事件)

    本文摘要: 1:什么是路由事件: 2:中断事件路由: 3:自定义路由事件: 4:为什么需要自定义路由事件: 5:什么是冒泡事件和预览事件(隧道事件): 1:什么是路由事件 WPF中的事件为路由事件,所 ...

  4. printf(),类型修饰符

    %s, 将输出作为字符串,要一直找到结束符'\0',这样在内存访问中,如果访问的是char 类型的,就会出现,从起始位置一直访问把整个申请的内存空间都遍历没有找到'\0',就会出现segment fa ...

  5. Executor框架(转载)

    Executor框架是指java 5中引入的一系列并发库中与executor相关的一些功能类,其中包括线程池,Executor,Executors,ExecutorService,Completion ...

  6. sprint2的总结及团队贡献分

    本次sprint做了订餐方法,用户可以通过搜索餐桌号进行点餐,查看已点的东西,也可以删除自己不想要的,当订单进入厨房时,厨房根据订单的顺序先后排列做餐,用户也可以通过扫描餐桌的二维码进行点餐. 148 ...

  7. kegg-kass注释--转载

    在注释KEGG的时候,一直用到kaas,具体kaas是个什么东东,简单的总结一下吧.     KEGG是由日本人搞的一个代谢图,收录基因和基因组的数据库,数据库可以分为 3大部分,基因数据库, 化学分 ...

  8. [bzoj2743][HEOI2012]采花(树状数组+离线)

    2743: [HEOI2012]采花 Time Limit: 15 Sec  Memory Limit: 128 MBSubmit: 1832  Solved: 954[Submit][Status] ...

  9. 通用访问 - 用“反射”来设计通用的通信协议,以及配套的SDK、工具

    1. 效果演示 2. 通信协议 功能介绍 特点 TCP协议 WebApi协议 3. SDK与工具 4. 应用示例 迷你网管 通用GIS 系统管理 5. 设计初衷与演化   1. 效果演示     服务 ...

  10. stm32串口输出丢失第一个字符的问题及原因

    因为TC和TXE 标志位在复位的时候会被置1,导致第一次没有发送. 所以在初始化串口的时候可以增加一句 USARTx->SR=0;(如 USART1->SR=0;)