您的位置:首页 > 其它

WCF简单教程(7) 并发模型与实例模型 推荐

2012-03-14 17:24 288 查看
第七篇:并发模型与实例模型

在以往使用WebService时,针对每一个请求,服务类总是并发响应的,并且对每个请求都生成新的实例。在WCF中,情况发生变化了,它允许服务发布者自定义并组合并发模型与实例模型。

并发模型有三种:

ConcurrencyMode

Single: 单线程模型,可以理解为,针对一个客户端,只有一个线程负责响应;

Reentrant:可重入的单线程模型,与Single的区别在于,对于OneWay/回调,它不会阻塞,而是把回调的线程放到队列尾部等着最后处理;

Multiple: 多线程模型,可以理解为,针对一个客户端,也允许并发访问;

实例模型也有三种:

InstanceContextMode

PerCall: 针对每次调用都生成新的服务实例;

PerSession:针对一个会话生成一个服务实例;

Single: 针对所有会话和所有调用共用同一个服务实例;

组合起来是什么效果呢?我们来用示例代码验证一下。

1、服务端

服务契约,具体解释见实现类吧,只要注意一下Sleep方法定义成了IsOneWay=true:

using System;
using System.ServiceModel;
using System.ServiceModel.Description;

namespace Server
{
[ServiceContract(Namespace = "WCF.Demo")]
public interface IData
{
[OperationContract]
int GetCounter();

[OperationContract(IsOneWay = true)]
void Sleep();
}
}


契约实现类:

using System;
using System.ServiceModel;
using System.ServiceModel.Description;
using System.Threading;
using System.Net;

namespace Server
{
//Single并发模式 + PerCall实例模式,针对后面的测试要修改这两个值的组合
[ServiceBehavior(ConcurrencyMode = ConcurrencyMode.Single, InstanceContextMode = InstanceContextMode.PerCall)]
public class DataProvider : IData
{
//定义一个计数器,对每个新生成的服务实例,它都是0,我们通过它来判断是否新实例
public int Counter { get; set; }

//获取计数器,并自增计数器
public int GetCounter()
{
return ++Counter;
}

//睡眠2秒
public void Sleep()
{
Thread.Sleep(2000);
}
}
}


App.config不列了,用的是netTcpBinding。

2、客户端:

别的不列了,只列一下调用代码:

using System;
using System.ServiceModel;
using System.ServiceModel.Channels;
using System.Threading;

namespace Client
{
class Program
{
static void Main(string[] args)
{
//启动3个线程并发访问
for(int i = 0; i < 3; ++i)
{
var thread = new Thread(() =>
{
string name = Thread.CurrentThread.Name;

var proxy = new ChannelFactory<Server.IData>("DataProvider").CreateChannel();

//先调用GetCounter方法,再调用Sleep方法,然后再调一次GetCounter方法
Console.WriteLine(string.Format("{0}: {1}  {2}", name, proxy.GetCounter(), DateTime.Now.ToString("HH:mm:ss.fff")));
proxy.Sleep();
Console.WriteLine(string.Format("{0}: {1}  {2}", name, proxy.GetCounter(), DateTime.Now.ToString("HH:mm:ss.fff")));
((IChannel)proxy).Close();
});

//定义一下线程名,方便识别
thread.Name = "线程" + i;
thread.Start();
}
}
}
}


OK,开始验证:

1、ConcurrencyMode.Single + InstanceContextMode.PerCall

执行结果如下:

线程1: 1  15:56:05.262
线程2: 1  15:56:05.262
线程0: 1  15:56:05.263
线程1: 1  15:56:07.263
线程2: 1  15:56:07.263
线程0: 1  15:56:07.264


首先,打印出的Counter全是1,说明针对每次请求,服务端的契约实现类(DataProvider)都是新实例化的。其次,同一个线程的两次GetCounter请求相隔了2秒,说明针对一个客户端的调用阻塞了。再次,三个线程几乎同时完成调用,说明它们之间并未互相阻塞。

2、ConcurrencyMode.Single + InstanceContextMode.PerSession

执行结果如下:

线程0: 1  16:02:46.173
线程1: 1  16:02:46.173
线程2: 1  16:02:46.173
线程1: 2  16:02:48.174
线程2: 2  16:02:48.174
线程0: 2  16:02:48.174

与上面相比,区别在于同一个线程的Counter在第二次调用时变成2了,说明针对同一个客户端的两次调用使用的是同一个服务实例。

3、ConcurrencyMode.Single + InstanceContextMode.Single

执行结果如下:

线程1: 2  16:05:46.270
线程0: 1  16:05:46.270
线程2: 3  16:05:46.270
线程1: 4  16:05:52.273
线程0: 5  16:05:52.273
线程2: 6  16:05:52.274


与上面相比,区别在于Counter一直在增长,这说明在服务端自始至终只有一个服务实例,它来响应所有的会话所有的请求。

4、ConcurrencyMode.Reentrant + InstanceContextMode.PerCall

执行结果如下:

线程1: 1  16:07:42.505
线程2: 1  16:07:42.506
线程2: 1  16:07:42.507
线程1: 1  16:07:42.507
线程0: 1  16:07:42.505
线程0: 1  16:07:42.507

和1的区别在于两次GetCounter调用之间没有2秒的延迟,这是由于Reentrant模式下,回调被放入队列尾部再处理,不会阻塞后面的调用。并且针对同一客户端的每个请求都是不同的服务实例在处理,不会阻塞。

5、ConcurrencyMode.Reentrant + InstanceContextMode.PerSession

执行结果如下:

线程2: 1  16:27:44.699
线程0: 1  16:27:44.700
线程1: 1  16:27:44.699
线程0: 2  16:27:46.700
线程1: 2  16:27:46.700
线程2: 2  16:27:46.700


与上面相比,区别在于又有了2秒的阻塞,这是由于针对一个客户端的多次请求,是同一个服务实例在处理,虽然允许重入,但只有一个对象,执行顺序是:第一次GetCounter->Sleep(不阻塞)->Sleep回调->第二次GetCounter,所以表现上还是阻塞住了。

6、ConcurrencyMode.Reentrant + InstanceContextMode.Single

执行结果如下:

线程0: 1  16:34:01.417
线程1: 3  16:34:01.418
线程2: 2  16:34:01.417
线程0: 4  16:34:05.420
线程1: 5  16:34:07.420
线程2: 6  16:34:07.420

自始至终只有一个服务实例,执行顺序应该是:线程0的GetCounter->线程2的GetCounter->线程1的GetCounter->线程0的Sleep(不阻塞)->线程2的Sleep(不阻塞)->线程0的Sleep回调->线程1的Sleep(不阻塞)->线程0的GetCounter->线程2的Sleep回调->线程1的Sleep回调->线程1的GetCounter->线程2的GetCounter。挺晕的。

7、ConcurrencyMode.Multiple + InstanceContextMode.PerCall

执行结果如下:

线程2: 1  17:07:05.639
线程1: 1  17:07:05.639
线程0: 1  17:07:05.639
线程2: 1  17:07:05.640
线程1: 1  17:07:05.641
线程0: 1  17:07:05.641


多次调用完全是并发的,每次调用的实例也是新创建的。

8、ConcurrencyMode.Multiple + InstanceContextMode.PerSession

执行结果如下:

线程1: 1  17:09:10.285
线程0: 1  17:09:10.285
线程1: 2  17:09:10.286
线程0: 2  17:09:10.286
线程2: 1  17:09:10.285
线程2: 2  17:09:10.287

多次调用完全并发,但针对同一个会话,实例是相同的。

9、ConcurrencyMode.Multiple + InstanceContextMode.Single

执行结果如下:

线程1: 1  17:16:46.543
线程0: 3  17:16:46.543
线程2: 2  17:16:46.543
线程1: 4  17:16:46.544
线程0: 5  17:16:46.544
线程2: 6  17:16:46.544


完全并发,Counter也一直增长,表明自始至终是同一个服务实例。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签:  wcf