WCF ConcurrencyMode.Multiple服务为何需先发首请求才支持并发?
WCF并发处理异常问题解析
我搭建了一个WCF服务,核心配置如下:
- 并发模式:
ConcurrencyMode = Multiple - 绑定方式:
WSHttpBinding - 实例上下文模式:
InstanceContextMode.PerSession
服务端代码
[ServiceBehavior(ConcurrencyMode = ConcurrencyMode.Multiple, InstanceContextMode = InstanceContextMode.PerSession)] public class StudentService : IStudentService { public string GetStudentInfo(int studentId) { Console.WriteLine(string.Format("{0} - [GetStudentInfo] Receive request, thread id {1}", DateTime.Now, System.Environment.CurrentManagedThreadId)); Thread.Sleep(5000); // 模拟业务耗时 string studentName = string.Empty; switch (studentId) { case 1: studentName = "Johnson"; break; case 2: studentName = "Henry"; break; default: studentName = "No student found"; break; } Console.WriteLine(string.Format("{0} - [GetStudentInfo] Leave request, thread id {1}", DateTime.Now, System.Environment.CurrentManagedThreadId)); return studentName; } }
现象描述
通过客户端多线程发送请求时,日志显示服务会顺序处理所有请求;但如果先发送一次请求,后续的多线程请求就能被并发处理。
客户端初始代码(顺序处理版本)
public static T CreateChannelFactory<T>() { try { ChannelFactory<T> myChannelFactory = new ChannelFactory<T>(new WSHttpBinding()); myChannelFactory.Endpoint.Address = new EndpointAddress("http://localhost:9091/StudentService"); T channel = myChannelFactory.CreateChannel(); return channel; } catch (Exception ex) { throw ex; } } static void Main(string[] args) { StudentService.IStudentService channel = CreateChannelFactory<StudentService.IStudentService>(); Thread[] arr = new Thread[10]; for (int i = 0; i < arr.Length; i++) { arr[i] = new Thread(new ThreadStart(() => channel.GetStudentInfo(1))); } for (int i = 0; i < arr.Length; i++) { arr[i].Start(); } for (int i = 0; i < arr.Length; i++) { arr[i].Join(); } Console.ReadKey(); }
修改后客户端代码(并发处理版本)
static void Main(string[] args) { StudentService.IStudentService channel = CreateChannelFactory<StudentService.IStudentService>(); channel.GetStudentInfo(1); // 提前发送一次请求 Thread[] arr = new Thread[10]; for (int i = 0; i < arr.Length; i++) { arr[i] = new Thread(new ThreadStart(() => channel.GetStudentInfo(1))); } for (int i = 0; i < arr.Length; i++) { arr[i].Start(); } for (int i = 0; i < arr.Length; i++) { arr[i].Join(); } Console.ReadKey(); }
服务宿主代码
static void Main(string[] args) { ServiceHost studentServiceHost = null; try { Uri httpBaseAddress = new Uri("http://localhost:9091/StudentService"); studentServiceHost = new ServiceHost(typeof(StudentService.StudentService), httpBaseAddress); studentServiceHost.AddServiceEndpoint(typeof(StudentService.IStudentService), new WSHttpBinding(), ""); ServiceMetadataBehavior serviceBehavior = new ServiceMetadataBehavior(); serviceBehavior.HttpGetEnabled = true; studentServiceHost.Description.Behaviors.Add(serviceBehavior); studentServiceHost.Open(); Console.WriteLine("Service is live now at : {0}", httpBaseAddress); Console.ReadKey(); studentServiceHost.Close(); } catch (Exception ex) { studentServiceHost = null; Console.WriteLine("There is an issue with StudentService:" + ex.Message); } }
通道工厂创建代码
public static T CreateChannelFactory<T>() { try { ChannelFactory<T> myChannelFactory = new ChannelFactory<T>(new WSHttpBinding()); myChannelFactory.Endpoint.Address = new EndpointAddress("http://localhost:9091/StudentService"); T channel = myChannelFactory.CreateChannel(); return channel; } catch (Exception ex) { throw ex; } }
问题
服务已配置ConcurrencyMode = Multiple,为何必须先发送一次请求,后续多线程请求才能被并发处理?
原因解析
核心原因在于WSHttpBinding的安全会话建立机制:
WSHttpBinding默认启用SecurityMode.Message安全会话,会话需要通过第一次请求完成密钥协商、上下文同步等初始化流程。- 初始客户端代码中,多线程同时调用通道方法时,会话尚未建立,所有请求会竞争触发会话建立逻辑——但WCF通道为保证会话建立的原子性,会自动序列化所有未在会话内的请求,只有当第一个请求完成会话建立后,后续请求才能进入服务实例的处理流程,因此表现为顺序执行。
- 提前发送一次请求后,客户端与服务端的安全会话已完全建立,通道进入就绪状态。此时
ConcurrencyMode.Multiple的配置正式生效:同一个会话对应的服务实例允许被多个线程同时调用,后续多线程请求无需等待会话协商,直接在已建立的会话上并发处理,因此呈现并发执行的效果。
补充说明:InstanceContextMode.PerSession意味着每个客户端会话对应一个独立服务实例,ConcurrencyMode.Multiple允许该实例同时处理多个请求,但前提是会话已完成初始化,否则通道的请求序列化逻辑会优先保障会话建立的正确性。
内容的提问来源于stack exchange,提问作者Hau Vo
相关产品推荐
相关产品推荐

