C# WCF并发处理.docx
《C# WCF并发处理.docx》由会员分享,可在线阅读,更多相关《C# WCF并发处理.docx(16页珍藏版)》请在冰豆网上搜索。
![C# WCF并发处理.docx](https://file1.bdocx.com/fileroot1/2022-11/16/7ee2699d-94e0-45ed-9f13-7f311c080abf/7ee2699d-94e0-45ed-9f13-7f311c080abf1.gif)
C#WCF并发处理
当多个线程同时访问相同的资源的时候就会产生并发,WCF缺省情况下会保护并发访问。
对并发访问需要恰当处理,控制不好不仅会大大降低WCF服务的吞吐量和性能,而且还有可能会导致WCF服务的死锁。
一、WCF并发模型:
在WCF中使用ServiceBehaviorAttribute中当多个线程同时访问相同的资源的时候就会产生并发,WCF缺省情况下会保护并发访问。
对并发访问需要恰当处理,控制不好不仅会大大降低WCF服务的吞吐量和性能,而且还有可能会导致WCF服务的死锁。
一、WCF并发模型:
在WCF中使用 ServiceBehaviorAttribute中的ConcurrencyMode属性来控制这个设置。
ConcurrencyMode属性是个枚举类型,有三个值:
ConcurrencyMode.Single、ConcurrencyMode.Reentrant和ConcurrencyMode.Multiple
publicenum ConcurrencyMode
{
Single,//默认,单线程模型
Reentrant,//单线程,可重入模型,通常用在CallBack调用模型中
Multiple//多线程模型
}
[AttributeUsage(AttributeTargets.Class)]
publicsealedclass ServiceBehaviorAttribute :
...
{
public ConcurrencyMode ConcurrencyMode
{get;set;}
//Moremembers
}
1.ConcurrencyMode.Single
单线程处理模式,同一个服务实例不会同时处理多个请求。
当服务在处理请求时会对当前服务加锁,如果再有其它请求需要该服务处理的时候,需要排队等候。
当服务处理完请求后会自动解锁,队列中的下个请求获取服务资源,继续处理。
例如:
我们去银行去办理业务,如果营业厅中只有一个窗口对外服务的话,那当前窗口每次只能处理一个用户请求,如果再有其它用户需要办理业务的话,只能排队等待。
直到我办理完业务后,营业窗口才能为队列中下个用户提供服务。
2.ConcurrencyMode.Reentrant
可重入的单线程处理模式,它仍然是单线程处理。
服务端一次仍然只能处理一个请求,如果有多个请求同时到达仍然需要排队。
与单线程不同的是,请求在处理过程中可以去调用其它服务,等到其它服务处理完成后,再回到原服务等待队列尾排队。
在调用其它服务的过程中,会暂时释放锁,其它等待线程会趁机进行服务的调用。
这种模式常见于服务端回调客户端的场境中。
例如:
我们去银行办理业务,营业厅中还是只有一个窗口对外服务,一次只能处理一个用户请求。
我向银行服务员请求办理开户业务,从此刻开始该服务被我锁定,其它人员只能排队等待。
在服务员办理业务的过程中,会让我填写开户申请表,这个签字的过程就是服务端对客户端的回调过程。
由于填写开户申请表的时间会很长,为了不耽搁后面排队顾客的时间,我暂时释放对服务员的锁定,到旁边填写申请表,让后面的人员办理业务。
在我签完字后再回到队列中等待服务,当再轮到我的时候我再次锁定服务,把凭据交给服务员,让他继续处理我的业务,这相当于服务的“重入”过程。
等到业务办完后,我会离开银行柜台,释放对该服务的锁定,等待队列中的下个人员又开始锁定服务办理业务了。
3.ConcurrencyMode.Multiple
多线程模式,多线程模式可以很好地增加系统的吞吐量。
当多个用户请求服务实例时,服务并不会加锁,而是同时为多个请求服务。
这样一来对所有用户共享的资源就会产生影响,所以这种多线程的访问模式需要对共享资源做好保护,大部份的情况下需我们的手动编写代码来实现多线程之间的访问保护。
例如:
我们去吃烤肉串,烤肉串的师傅可以同时为多个顾客烤制,而不用一个一个地排队等待,这就是典型的多线程处理模式。
但这种模式如果不对肉串很好保护得的话那可麻烦了,比仿,我要的是3串麻辣味的,你要的是3串香辣味的,他要的是3串原味的,烤肉的时傅在烤制的时候需要对这三份肉串做好保护,防止做出一些“五味俱全”的肉串来。
二、WCF中的并发模型与实例模型的关系。
实例模型与并发模型的关系很密切,对于不同的实例模型,并发所需要的并发管理也不一样。
1.ServiceBehavior(ConcurrencyMode=ConcurrencyMode.Single,InstanceContextMode=InstanceContextMode.PerCall)--Single并发与PerCall实例模型
《图1》
对于PerCall的实例模型,每个客户端请求都会与服务端的一个独立的服务实例进行交互,就不会出现多个客户端请求争用一个服务实例的情况,也就不会出现并发冲突。
也就是说这时候ConcurrencyMode=ConcurrencyMode.Single写与不写都没关系,不会影响吞吐量的问题。
Single并发是系统默认的,而PerCall实例模型也是系统默认的。
也就是说在默认情况下,WCF服务不会出现并发访问冲突的情况,当然对于静态数据或全局缓存数据会产生并发冲突,后面会谈到如何解决这个问题。
当然在进行回调操作的时候,有可能会出现等待超时的问题。
这在下面4会谈到。
服务端代码:
[ServiceContract]
publicinterface ISinglePerCall
{
[OperationContract]
int GetValue1();
[OperationContract]
int GetValue2();
}
[ServiceBehavior(InstanceContextMode=InstanceContextMode.PerCall,ConcurrencyMode=ConcurrencyMode.Single)]
publicclass SinglePerCall :
ISinglePerCall
{
privateint InstanceVariable =0;
private static int StaticVariable =0;
publicintGetValue1()
{
return ++InstanceVariable;
}
publicintGetValue2()
{
return ++StaticVariable;
}
}
这里我们定了服务契约ISinglePerCall,它里面包含了两个方法GetValue1()和GetValue2()。
GetValue1()的主要作用是把实例变量的值加1返回;GetValue2()的主要作用是把静态变量的值加1返回。
服务的行为模式是:
Single并发+PerCall实例
由于PerCall实例模式会为每个请求生成一个新的服务实例,所以,我们调用GetValue1()返回的结果应当永远都是1。
因为每个服务实例在生成的时候都默认为InstanceVariable设为0;但调用GetValue2()的时候并不会都是1,因为GetValue2()读取的是静态变量自增后的值,静态变量被所有实例所共享,所以它会从1开始递增。
由于在这里多个服务实例同时运行,对共享的静态变量的访问有可以产生冲突,故会出现下面的运行结果。
要解决这个问题还需要我们手动编写同步代码。
客户端代码:
classSinglePerCall
{
privatestaticSRSinglePerCall.SinglePerCallClientclient=newClient.SRSinglePerCall.SinglePerCallClient();
publicstaticvoidMain(string[]args)
{
for(inti=0;i<10;i++)
{
Threadthread=newThread(newThreadStart(DoWork));
thread.Start();
while(!
thread.IsAlive);
}
}
publicstaticvoidDoWork()
{
Console.WriteLine("线程"+Thread.CurrentThread.GetHashCode()+":
/t实例变量的值:
"+client.GetValue1());
Console.WriteLine("线程"+Thread.CurrentThread.GetHashCode()+":
/t静态变量的值:
"+client.GetValue2());
}
}
在客户端中我启用了10个线程向服务端发起调用,并显示线程号、服务端实例变量的结果和静态变量的结果。
运行结果:
《图1-1》
从图中我们看到实例变量的值都是1,静态变量的值是出现递增的情形,但由于没有采取同步策略,有可能会出现“线程9”和“线程11”这样的并发冲突的情况。
2.ServiceBehavior(ConcurrencyMode=ConcurrencyMode.Single,InstanceContextMode=InstanceContextMode.PerSession)--Single并发与PerSession实例模型
《图2》
对于PerSession实例模型,每个客户端对应一个服务实例,这样在不同客户端之间不会出现服务实例并发调用的情况,但每个客户端可以采用多线程调用同一个服务实例。
由于我们采用了ConcurrencyMode.PerSession并发模式,这使得多线程在调用同一服务实例的时候会进行线程排队,服务每次只对一个线程进行处理。
Single并发模式对多线程的客户端的吞吐量会带来影响,而对多个单线程的客户端访问并不起作用。
服务端代码:
[ServiceContract(SessionMode=SessionMode.Required)]
publicinterface ISinglePerSessison
{
[OperationContract]
int GetValue1();
[OperationContract]
int GetValue2();
}
[ServiceBehavior(InstanceContextMode=InstanceContextMode.PerSession,ConcurrencyMode=ConcurrencyMode.Single)]
publiccla