第七篇:并发模型与实例模型
在以往使用WebService时,针对每一个请求,服务类总是并发响应的,并且对每个请求都生成新的实例。在WCF中,情况发生变化了,它允许服务发布者自定义并组合并发模型与实例模型。
并发模型有三种:
ConcurrencyMode Single: 单线程模型,可以理解为,针对一个客户端,只有一个线程负责响应; |
实例模型也有三种:
InstanceContextMode PerCall: 针对每次调用都生成新的服务实例; |
组合起来是什么效果呢?我们来用示例代码验证一下。
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也一直增长,表明自始至终是同一个服务实例。
本文转自 BoyTNT 51CTO博客,原文链接:http://blog.51cto.com/boytnt/806014,如需转载请自行联系原作者