WCF中的并发针对服务而言。而服务实例封装在InstanceContext,所以WCF中的并发最终体现在了InstanceContext中。WCF服务实例上下文模式InstanceContextMode又决定服务以何种方法为客户端提供服务。
实例模式 :
public
enum InstanceContextMode
{
PerSession,
PerCall,
Single
}
PerSession:每次会话都是使用同一个服务实例
PerCall:每次调用都创建新的服务实例对象
Single:所有服务都是用同一个服务实例对象,相当于所有客户端代理都使用服务端同一个静态服务实例对象为其服务
默认情况下,InstanceContextMode默认为PerSession
并发部分:
服务通过ServiceBehaviorAttribute的ConcurreceMode属性定义了三种不同的并发模型:
public
enum ConcurrencyMode
{
Single,
Reentrant,
Multiple
}
Single:封装服务实例的InstanceContext某时刻只处理一个客户端请求,其他的请求会被放入请求队列中等待处理。如果多个客户端对服务并发访问,这些服务最终在服务端也是通过串行化进行处理。如果之前的请求处理完毕,队列中的第一个请求会被处理。它的实现是对InstanceContext继承自CommucationObject中ThisLock
Reentrant:该模式与Single一样,某时刻只能处理一个客户端请求。与Single不同的是:如果在处理 请求A时,某时刻服务端回调客户端,此时服务可继续处理下一个请求B。当回调客户端完成返回服务端,如果B请求没有处理完,则A请求需要继续等待直至请求B释放锁,此时如果A能获取到锁,则它的请求被继续执行。
Multiple:在该模式下,封装服务实例的一个InstanceContext能同时处理多个请求。
默认情况下,ConcurrencyMode 为Single。
下面测试一下几种实例与并发模式
服务契约定义:
[ServiceContract]
public
interface IAdd
{
[OperationContract]
void Add(
int x,
int y);
}
服务实现:
public
class AddService : IAdd
{
private
readonly
int counter =
0;
public AddService()
{
counter++;
Console.ResetColor();
Console.WriteLine(
string.Format(
"
AddService Construction function excute... counter is {0}
", counter));
}
#region IAdd 成员
public
void Add(
int x,
int y)
{
var clientId = OperationContext.Current.IncomingMessageHeaders.GetHeader<
int>(MessageWrapper.headerClientId,
MessageWrapper.headerNamespace);
Console.ForegroundColor = ConsoleColor.Red;
Console.WriteLine(
string.Format(
"
Time:{0};ThreadId is :{1}.Request Id is {2} Add Method Invoked,
", DateTime.Now,Thread.CurrentThread.ManagedThreadId,clientId));
Console.WriteLine(
string.Format(
"
result is : {0}
",x + y));
Thread.Sleep(
5000);
Console.WriteLine(
"
=========Excute finished=========
");
Console.WriteLine();
}
#endregion
}
服务端配置:
<system.serviceModel>
<services>
<service name="Service.AddService">
<endpoint address="http://127.0.0.1:3636/AddService" binding="basicHttpBinding" contract="Contract.IAdd"></endpoint>
</service>
</services>
</system.serviceModel>
客户端配置这里不再给出。 客户端调用代码:
ChannelFactory<IAdd> factory=
new ChannelFactory<IAdd>(
"
AddService
");
IAdd proxy = factory.CreateChannel();
for (int i = 0; i < 5; i++)
{
ThreadPool.QueueUserWorkItem(delegate
{
int clientId = Interlocked.Increment(ref index);
using (OperationContextScope contextScope =
new OperationContextScope(proxy as IContextChannel))
{
MessageHeader<int> header = new MessageHeader<int>(clientId);
System.ServiceModel.Channels.MessageHeader messageHeader =
header.GetUntypedHeader(MessageWrapper.headerClientId,
MessageWrapper.headerNamespace);
OperationContext.Current.OutgoingMessageHeaders.Add(messageHeader);
proxy.Add(1, 2);
}
});
}
测试1:PreCall + Multiple:
输出如下:
图1
由以上输出,服务对客户端调用是以串行的方式进行的。 以上我们设置的ConcurrencyMode.Muliple。明明是并发模式,为何服务端却以串行的方式执行呢。?这是因为如果服务代理以自动开启的方式(即进行调用时,如果代理没有打开,调用时会打开代理)进行并发调用,WCF需要对调用进行序列化知道代理打开之后在进行并发处理。如果将代理显示开启,则就能显示并发调用结果。对代码进行如下修改:
IAdd proxy = factory.CreateChannel();
if (
null != proxy
as ICommunicationObject)
{
(proxy
as ICommunicationObject).Open();
}
for ()
{
/**/
}
服务端输出如下:
图2
测试2:PerSession + Single/Reentrant
1、将绑定协议换成支持会话的绑定,如wsHttpBinding,服务端输出如上图1
2、将绑定协议换成不支持会话的绑定,如basicHttpBind个ing ,服务端输出如上图2
由此可知:
PerSession+Single/Reentrant下:Allowed+不支持会话的绑定,此时为服务端并发处理请求。
Allowed
+支持会话的绑定,此时为服务端串行处理请求(SessionMode不进行设置时,默认为Allowed).
测试3: Single + Single
将服务实例模式改为Single,即
[ServiceBehavior(InstanceContextMode=InstanceContextMode.Single)]
此时服务端输出如图3:
图3
由输出可知:
Single+Single/Reentrant下, 服务端串行处理请求
测试4: Single + Multiple
将服务实例模式改为Single,并发模式为Multiple即
[ServiceBehavior(InstanceContextMode=InstanceContextMode.Single,ConcurrencyMode = ConcurrencyMode.Multiple)]
此时服务端输出如下:
图4
测试5:PerCall+ Single/Reentrant/Multiple
将服务实例模式改为PerCall,无论并发模式如何,此时中表现为并发。对WCF而言,并发是对实例上下文InstanceContext而言的。无论对于相同客户端调用(相同服务代理)还是不同客户端调用请求,WCF客户端都会重新生成InstanceContext,通过封装在其中的服务实例进行对调用的处理,因此不会存在对InstanceContext的并发调用.
[ServiceBehavior(InstanceContextMode=InstanceContextMode.PerCall)]
此时总为并发:此时服务端输出如下:
图5
由以上分析对于不同实例模式,以及并发模式总结如下图: