《WCF按部就班学习系列8_WCF并发管理概述》(上)

来源:互联网 发布:密歇根理工大学 知乎 编辑:程序博客网 时间:2024/06/06 06:42

《WCF按部就班学习系列8_WCF并发管理概述》,本文的主要结构为:1)WCF并发管理的概述2)WCF服务并发模式、WCF实例模型和并发模型关系、限流概述3)实现代码分析及运行结果4)源码下载5)下一篇计划6)参考说明
(1)WCF并发管理的概述(what)
1.当多个线程同时访问相同的资源的时候就会产生并发,WCF缺省情况下会保护并发访问。
2.对并发访问需要恰当处理,控制不好不仅会大大降低WCF服务的吞吐量和性能,而且还有可能会导致WCF服务的死锁。
3.如果需要的话,并发管理能够确保服务(或者服务访问的资源)执行在特定的线程上。
4..ServiceBehavior特性的ConcurrencyMode属性负责管理服务实例的并发访问:
ConcurrencyMode属性是个枚举类型,有三个值:ConcurrencyMode.Single、ConcurrencyMode.Reentrant和ConcurrencyMode.Multiple

ConcurrencyMode枚举值负责控制并发调用是否允许以及何时应用到服务实例上。
5.WCF提供了两种同步模式。自动同步(Automatic Synchronization)要求WCF同步访问服务实例。它的实现非常简单,易于使用,但只能用于服务与回调类。
手动同步( Manual Synchronization)增加了开发者实现同步的负担,要求实现特定的应用程序集成。开发者必须使用.NET同步锁,这也是目前实现同步的一条专门的原则。
手动同步的优势在于它可以用于服务和类似于服务的非服务类,允许开发者优化吞吐量与可伸缩性。

(2)WCF服务并发模式、WCF实例模型和并发模型关系、限流概述
2.1WCF服务并发模式
【1】ConcurrencyMode.Single
如果服务被设置为ConcurrencyMode.Single.WCF会为服务实例提供自动同步,并通过关联服务实例与同步锁以禁止并发调用。
单线程处理模式,同一个服务实例不会同时处理多个请求。当服务在处理请求时会对当前服务加锁,如果再有其它请求需要该服务处理的时候,需要排队等候。当服务处理完请求后会自动解锁,队列中的下个请求获取服务资源,继续处理。
例如:我们去银行去办理业务,如果营业厅中只有一个窗口对外服务的话,那当前窗口每次只能处理一个用户请求,如果再有其它用户需要办理业务的话,只能排队等待。直到我办理完业务后,营业窗口才能为队列中下个用户提供服务。
【2】ConcurrencyMode.Reentrant
可重入的单线程处理模式,它仍然是单线程处理。服务端一次仍然只能处理一个请求,如果有多个请求同时到达仍然需要排队。与单线程不同的是,请求在处理过程中可以去调用其它服务,等到其它服务处理完成后,再回到原服务等待队列尾排队。在调用其它服务的过程中,会暂时释放锁,其它等待线程会趁机进行服务的调用。这种模式常见于服务端回调客户端的场境中。
例如:我们去银行办理业务,营业厅中还是只有一个窗口对外服务,一次只能处理一个用户请求。我向银行服务员请求办理开户业务,从此刻开始该服务被我锁定,其它人员只能排队等待。

在服务员办理业务的过程中,会让我填写开户申请表,这个签字的过程就是服务端对客户端的回调过程。由于填写开户申请表的时间会很长,为了不耽搁后面排队顾客的时间,我暂时释放对服务员的锁定,到旁边填写申请表,让后面的人员办理业务。在我签完字后再回到队列中等待服务,当再轮到我的时候我再次锁定服务,把凭据交给服务员,让他继续处理我的业务,这相当于服务的“重入”过程。 等到业务办完后,我会离开银行柜台,释放对该服务的锁定,等待队列中的下个人员又开始锁定服务办理业务了。
【3】ConcurrencyMode.Multiple
多线程模式,多线程模式可以很好地增加系统的吞吐量。当多个用户请求服务实例时,服务并不会加锁,而是同时为多个请求服务。这样一来对所有用户共享的资源就会产生影响,所以这种多线程的访问模式需要对共享资源做好保护,大部份的情况下需我们的手动编写代码来实现多线程之间的访问保护。
例如:我们去吃烤肉串,烤肉串的师傅可以同时为多个顾客烤制,而不用一个一个地排队等待,这就是典型的多线程处理模式。但这种模式如果不对肉串很好保护得的话那可麻烦了,比仿,我要的是3串麻辣味的,你要的是3串香辣味的,他要的是3串原味的,烤肉的时傅在烤制的时候需要对这三份肉串做好保护,防止做出一些“五味俱全”的肉串来。
2.2WCF实例模型和并发模型关系
实例模型与并发模型的关系很密切,对于不同的实例模型,并发所需要的并发管理也不一样。
【1】ServiceBehavior(ConcurrencyMode = ConcurrencyMode.Single,InstanceContextMode = InstanceContextMode.PerCall) --Single并发与PerCall实例模型
1.图8-1单调模式和单线程描述

2.对于PerCall的实例模型,每个客户端请求都会与服务端的一个独立的服务实例进行交互,就不会出现多个客户端请求争用一个服务实例的情况,也就不会出现并发冲突。也就是说这时候

ConcurrencyMode = ConcurrencyMode.Single写与不写都没关系,不会影响吞吐量的问题。
3.Single并发是系统默认的,而PerCall实例模型也是系统默认的。也就是说在默认情况下,WCF服务不会出现并发访问冲突的情况,当然对于静态数据或全局缓存数据会产生并发冲突,后面

会谈到如何解决这个问题。当然在进行回调操作的时候,有可能会出现等待超时的问题.
4.图8-2单调模型和单线程运行结果

5.从图8-2中我们看到实例变量的值都是1,静态变量的值是出现递增的情形,但由于没有采取同步策略,有可能会出现“线程9”和“线程11”这样的并发冲突的情况。

【2】ServiceBehavior(ConcurrencyMode = ConcurrencyMode.Single,InstanceContextMode = InstanceContextMode.PerSession) --Single并发与PerSession实例模型
1.图8-3会话模型和单线程描述

2.对于PerSession实例模型,每个客户端对应一个服务实例,这样在不同客户端之间不会出现服务实例并发调用的情况,但每个客户端可以采用多线程调用同一个服务实例。
3.由于我们采用了ConcurrencyMode.PerSession并发模式,这使得多线程在调用同一服务实例的时候会进行线程排队,服务每次只对一个线程进行处理。
Single并发模式对多线程的客户端的吞吐量会带来影响,而对多个单线程的客户端访问并不起作用。
4.由于使用的PerSession实例模式,所以每个客户端共享同一个服务实例,当同一客户端使用多线程来访问服务的话,会访问同一个InstanceVariable变量。由于是Single并发模式,所以不

会对InstanceVariable变量产生并发冲突。
5.图8-4会话模型和单线程运行结果

6.从图8-4中可以看出,静态变量依然是被不同客户端不同的线程所共享;实例变量在同一个客户端的多个线程中是共享的,在不同客户端是各自独立的。
7.在Single并发+PerSession实例中,实例变量不会产生并发冲突,而静态变量可能会在不同的客户端之间产生并发冲突,在同一客户端多线程间不会产生并发冲突。
【3】ServiceBehavior(ConcurrencyMode = ConcurrencyMode.Single,InstanceContextMode = InstanceContextMode.Single) --Single并发与Single实例模型
1.图8-5单例模型和单线程描述

2.对于Single实例模型,多个客户端对应一个服务实例,这样就容易发生多个客户端同时请求一个服务进行处理,产生并发问题。当采取Single并发模式时,多个客户端的并发访问问题会得

到解决,因为Single并发模式会使多个客户端请求进行排队,依次处理每个客户端的请求。
3.Single并发模式在Single实例模型中,每次只能处理一个调用请求,会对系统的吞吐量带来很大的影响。
4.图8-6单例模型和单线程运行结果

5.由于我们把实例模型设为Single,那所有客户端所有线程调用同一服务实例,故这些线程共享InstanceVariable实例变量,由于并发模式是Single模式,所以不会对此变量产生并发冲突。

而对于StaticVariable静态变量,而言也是所有客户端线程所共享的,但这里的静态变量StaticVariable不会产生并发冲突,因为只有一个服务实例来操作此静态变量。
【4】ServiceBehavior(ConcurrencyMode = ConcurrencyMode.Reentrant,InstanceContextMode = InstanceContextMode.PerCall) --Reentrant并发与PerCall实例模型
1.图8-7单调模型和单线程可重入模型描述

2.要解决这个问题,我们可以采用Reentrant并发模式,Reentrant模式依然是单线程模式,只是它允许客户端加调用能再回到服务端继续执行。
3.图8-8单调模型和单线程可重入模型运行结果

4.客户端:多线程发起调用时间是一样的,即向服务端发出的请求几乎是同一时间的。而线程结束的时间差别很大,之间几乎相差5秒。
服务端:确实有三个服务实例响应客户端的三个线程。但从其发起调用和调用结束的时间来看,这三个服务实例并非同时发起对客户端的回调,而是有严格的先后次序。
5.调用模型有三种:“请求-响应”(默认)、“One-Way”和“Duplex”。
这里我们没有明确指出采用哪种调用模式,因此为默认使用“请求-响应”方式。而“请求-响应”模式,客户端必须等服务端“响应”完成上一次“请求”后才能发出下一步“请求”。因此

虽然客户端使用多线程方式来调用服务,但最后的执行结果仍然表现出顺序处理。
6.要想使服务端能够并行处理客户端请求的话,那我们就不能使用“请求-响应”的调用模式,我们可以使用One-Way的方式来调用服务。
因此我们可以把服务端的ServiceMethod方法契约修正如下:
    [OperationContract(IsOneWay=true)]
    void ServiceMethod();
7.图8-9单调模型和单线程可重入模型(One-Way)运行结果

8.从图8-9中的结果我们看出
客户端:多线程的发起调用的时间是一样的,结束调用的时间也是一样的,并且发起调用和结束调用之间并没有时间延迟,这说明客户端多线程各自独立地向服务端发出调用成功。
服务端:确实在服务端产生三个实例处理客户端调用,并且这三个服务实例回调客户端的时间是相同的,但客户端回调结束的时间相差太大有明显的5秒延迟。
9.由于回调契约中的方法契约ClientMethod没有明确设置调用方式,所以也是默认“请求-响应”的调用方式,如果回调没有完成,则下个回调只能处于阻塞状态。因此导致服务端的回调虽然

同时发起,但结束回调的时间却差别很大。
下面我们在上面的基础上再试着把回调契约中的ClientMethod方法契约声明为One-Way
    [OperationContract(IsOneWay=true)]
    void ClientMethod();
10.图8-10单调模型和单线程可重入模型(One-Way(client))运行结果

11.从图8-10中我们可以看到
客户端:多线程异步调用
服务端:多线程异步调用,中间再没有发生延迟情况。
话又说回来,这种回调契约IsOneWay=true调用的方式却并不是ConcurrencyMode = ConcurrencyMode.Reentrant的并发模式。
12.上面的例子,我们是用一个代理的多个线程调用WCF服务,下面我们把客户端代码修改一下,实现多个代理用各自的线程调用WCF服务。为了能明确表达Reentrant并发模式,我们服务端方

法契约的IsOneWay=true去掉。
13.图8-11单调模型和单线程可重入模型(One-Way(C)和请求-响应(S))运行结果

14.从图8-11中可以看出,
在客户端三个服务被同时发出,调用结束后几乎同时返回。在服务端,也几乎同时向客户端发出回调,客户端在经过5秒的延迟后也几乎同时返回。休现了实例之间的无关。
15.所以在研究并发的时候不应当只研究“并发模型”和“实例模型”,还应当考虑到“调用模型”。
【5】ServiceBehavior(ConcurrencyMode = ConcurrencyMode.Reentrant,InstanceContextMode = InstanceContextMode.PerSession) --Reentrant并发与PerSession实例模型
1.图8-12会话模型和单线程可重入模型描述

2.而Reentrant并发模式,依然是个单线程的模式,即某时刻只能有一个线程调用服务实例,其余的线程处于等待队列中。但是当某线程回调客户端操作时,该线程就不再锁定该服务实例,等

待队列中的下个线程将锁定服务实例进行调用。当回调客户端的线程执行完客户端调用后再回到服务端时,它将进入线程等待队列中重新排队等待。
3.在“Reentrant并发+PerSession实例”模式中关于“调用方式(请求-响应、OneWay和Duplex)”对运行结果的影响,与上面“Reentrant并发+PerCall实例”模式中的“调用方式”对结果

的影响是一样的。
4.下面我们依然把服务契约中的方法契约调用设置为IsOneWay=true。
5.图8-13会话模型和单线程可重入模型(IsOneWay)运行结果

6.从图8-13中我们看到
客户端中我们使用同一个代理对象的多线程技术向服务端发送请求,在这里我启用了两个客户端。
而服务器端一共产生了两个实例,每个实例代表一个客户端的调用。在每个服务对象中,我们可以看到三个客户端回调的时间基本相同,而客户端回调返回的时间却相隔5秒钟。
【6】ServiceBehavior(ConcurrencyMode = ConcurrencyMode.Reentrant,InstanceContextMode = InstanceContextMode.Single) --Reentrant并发与Single实例模型
1.图8-14单例模型和单线程可重入模型描述

2.对于“Single实例模型+Reentrant并发模式”与“PerSession实例模型+Reentrant并发模式”有些相似,是单线程重入处理模式。如果调用方式是“请求响应”方式时,如果是同一个代理

的多个线程会受到阻塞,如果是多个代理则不同代理会并行调用;如果是OneWay调用模式,则不会出现任阻塞情况.
3.上面“Single实例模型+Reentrant并发模式”与“PerSession实例模型+Reentrant并发模式”这两个模型就与“去银行排队”的例子很相似。
4.图8-15单例模型和单线程可重入模型描述运行结果

5.从图8-15中我们看出两个客户端的六个线程的确调用服务端的同一个实例,用蓝色和绿色标出来的执行结果分别是不同客户端的线程回调情况。我们可以看出两个客户端之间是并行运行的

,而同一个客户端的三个线程则是排队调用的--因为用的是“请求-响应”的调用模式。
6.到目前为至我们研究完了PerCall和Reentrant两种并发模式,这两种并发模式都是单线程调用,每个调用都会独占其服务,因此并不会对服务实例产生并发调用的冲突。

(待续)