如果您想自己运行,我粘贴了以下代码. 我尝试过的测试用例: 600个线程调用服务500次 200个线程调用服务1000次 8个线程调用服务10000次 1个线程调用服务10000次我在运行Service 2008 R2的4 CPU VM上运行了此命令.除了1个线程外,其他所有线程都受CPU限制. 结果:所有的运行都在彼此的5%之内.有时ConcurrencyMode.Multiple更快.有时ConcurrencyMode.Single更快.也许适当的统计分析可以选出一个赢家.我认为它们足够接近而无所谓. 这是典型的输出:在net.pipe://localhost/base上启动 Single 服务... 类型= SingleService ThreadCount = 600 ThreadCallCount = 500 运行时间:45156759滴答声 12615毫秒在net.pipe://localhost/base上启动 Multiple 服务... 类型= MultiService ThreadCount = 600 ThreadCallCount = 500 运行时间:48731273滴答声 13613毫秒在net.pipe://localhost/base上启动 Single 服务... 类型= SingleService ThreadCount = 600 ThreadCallCount = 500 运行时间:48701509次滴答声:13605毫秒在net.pipe://localhost/base上启动 Multiple 服务... 类型= MultiService ThreadCount = 600 ThreadCallCount = 500 运行时:48590336滴答声:13574毫秒 基准代码: 通常的警告:这是基准代码,它采用了不适用于生产用途的捷径.using System;using System.Collections.Generic;using System.Linq;using System.ServiceModel;using System.ServiceModel.Description;using System.Text;using System.Threading;using System.Threading.Tasks;namespace WCFTest{ [ServiceContract] public interface ISimple { [OperationContract()] void Put(); } [ServiceBehavior(InstanceContextMode = InstanceContextMode.PerCall, ConcurrencyMode = ConcurrencyMode.Single)] public class SingleService : ISimple { public void Put() { //Console.WriteLine("put got " + i); return; } } [ServiceBehavior(InstanceContextMode = InstanceContextMode.PerCall, ConcurrencyMode = ConcurrencyMode.Multiple)] public class MultipleService : ISimple { public void Put() { //Console.WriteLine("put got " + i); return; } } public class ThreadParms { public int ManagedThreadId { get; set; } public ServiceEndpoint ServiceEndpoint { get; set; } } public class BenchmarkService { public readonly int ThreadCount; public readonly int ThreadCallCount; public readonly Type ServiceType; int _completed = 0; System.Diagnostics.Stopwatch _stopWatch; EventWaitHandle _waitHandle; bool _done; public BenchmarkService(Type serviceType, int threadCount, int threadCallCount) { this.ServiceType = serviceType; this.ThreadCount = threadCount; this.ThreadCallCount = threadCallCount; _done = false; } public void Run(string baseAddress) { if (_done) throw new InvalidOperationException("Can't run twice"); ServiceHost host = new ServiceHost(ServiceType, new Uri(baseAddress)); host.Open(); Console.WriteLine("Starting " + ServiceType.Name + " on " + baseAddress + "..."); _waitHandle = new EventWaitHandle(false, EventResetMode.ManualReset); _completed = 0; _stopWatch = System.Diagnostics.Stopwatch.StartNew(); ServiceEndpoint endpoint = host.Description.Endpoints.Find(typeof(ISimple)); for (int i = 1; i <= ThreadCount; i++) { // ServiceEndpoint is NOT thread safe. Make a copy for each thread. ServiceEndpoint temp = new ServiceEndpoint(endpoint.Contract, endpoint.Binding, endpoint.Address); ThreadPool.QueueUserWorkItem(new WaitCallback(CallServiceManyTimes), new ThreadParms() { ManagedThreadId = i, ServiceEndpoint = temp }); } _waitHandle.WaitOne(); host.Shutdown(); _done = true; //Console.WriteLine("All DONE."); Console.WriteLine(" Type=" + ServiceType.Name + " ThreadCount=" + ThreadCount + " ThreadCallCount=" + ThreadCallCount); Console.WriteLine(" runtime: " + _stopWatch.ElapsedTicks + " ticks " + _stopWatch.ElapsedMilliseconds + " msec"); } public void CallServiceManyTimes(object threadParams) { ThreadParms p = (ThreadParms)threadParams; ChannelFactory<ISimple> factory = new ChannelFactory<ISimple>(p.ServiceEndpoint); ISimple proxy = factory.CreateChannel(); for (int i = 1; i < ThreadCallCount; i++) { proxy.Put(); } ((ICommunicationObject)proxy).Shutdown(); factory.Shutdown(); int currentCompleted = Interlocked.Increment(ref _completed); if (currentCompleted == ThreadCount) { _stopWatch.Stop(); _waitHandle.Set(); } } } class Program { static void Main(string[] args) { BenchmarkService benchmark; int threadCount = 600; int threadCalls = 500; string baseAddress = "net.pipe://localhost/base"; for (int i = 0; i <= 4; i++) { benchmark = new BenchmarkService(typeof(SingleService), threadCount, threadCalls); benchmark.Run(baseAddress); benchmark = new BenchmarkService(typeof(MultipleService), threadCount, threadCalls); benchmark.Run(baseAddress); } baseAddress = "http://localhost/base"; for (int i = 0; i <= 4; i++) { benchmark = new BenchmarkService(typeof(SingleService), threadCount, threadCalls); benchmark.Run(baseAddress); benchmark = new BenchmarkService(typeof(MultipleService), threadCount, threadCalls); benchmark.Run(baseAddress); } Console.WriteLine("Press ENTER to close."); Console.ReadLine(); } } public static class Extensions { static public void Shutdown(this ICommunicationObject obj) { try { if (obj != null) obj.Close(); } catch (Exception ex) { Console.WriteLine("Shutdown exception: {0}", ex.Message); obj.Abort(); } } }}I always thought that setting InstanceContextMode to PerCall makes concurrency mode irrelevant even if using a session aware binding like net.tcp. This is what MSDN says http://msdn.microsoft.com/en-us/library/ms731193.aspx"In PerCallinstancing, concurrency is not relevant, because each message is processed by a new InstanceContext and, therefore, never more than one thread is active in the InstanceContext."But today I was going through Juval Lowy's book Programming WCF Services and he writes in Chapter 8 This is contradicting my understanding and what MSDN says. Which is correct ?In my case I have a WCF Net.Tcp service used my many client applications that creates a new proxy object, makes the call and then immediately closes the proxy. The service has PerCall InstanceContextMode. Will I get improved throughput if I change the InstanceContextMode to Multiple with no worse thread safety behaviour than percall ? 解决方案 The key phrase in reading Lowy’s statement is "in the interest of throughput". Lowy is pointing out that when using ConcurrencyMode.Single WCF will blindly implement a lock to enforce serialization to the service instance. Locks are expensive and this one isn’t necessary because PerCall already guarantees that a second thread will never try to call the same service instance.In terms of behavior:ConcurrencyMode does not matter for a PerCall service instance.In terms of performance: A PerCall service that is ConcurrencyMode.Multiple should be slightly faster because its not creating and acquiring the (unneeded) thread lock that ConcurrencyMode.Single is using.I wrote a quick benchmark program to see if I could measure the performance impact of Single vs Multiple for a PerCall service: The benchmark showed no meaningful difference.I pasted in the code below if you want to try running it yourself.Test cases I tried: 600 threads calling a service 500 times200 threads calling a service 1000 times8 threads calling a service 10000 times1 thread calling a service 10000 timesI ran this on a 4 CPU VM running Service 2008 R2. All but the 1 thread case was CPU constrained.Results: All the runs were within about 5% of eachother. Sometimes ConcurrencyMode.Multiple was faster. Sometimes ConcurrencyMode.Single was faster. Maybe a proper statistical analysis could pick a winner. In my opinion they are close enough to not matter. Here’s a typical output:Starting Single Service on net.pipe://localhost/base... Type=SingleService ThreadCount=600 ThreadCallCount=500 runtime: 45156759 ticks 12615 msecStarting Multiple Service on net.pipe://localhost/base... Type=MultipleService ThreadCount=600 ThreadCallCount=500 runtime: 48731273 ticks 13613 msecStarting Single Service on net.pipe://localhost/base... Type=SingleService ThreadCount=600 ThreadCallCount=500 runtime: 48701509 ticks 13605 msecStarting Multiple Service on net.pipe://localhost/base... Type=MultipleService ThreadCount=600 ThreadCallCount=500 runtime: 48590336 ticks 13574 msecBenchmark Code:Usual caveat: This is benchmark code that takes short cuts that aren’t appropriate for production use.using System;using System.Collections.Generic;using System.Linq;using System.ServiceModel;using System.ServiceModel.Description;using System.Text;using System.Threading;using System.Threading.Tasks;namespace WCFTest{ [ServiceContract] public interface ISimple { [OperationContract()] void Put(); } [ServiceBehavior(InstanceContextMode = InstanceContextMode.PerCall, ConcurrencyMode = ConcurrencyMode.Single)] public class SingleService : ISimple { public void Put() { //Console.WriteLine("put got " + i); return; } } [ServiceBehavior(InstanceContextMode = InstanceContextMode.PerCall, ConcurrencyMode = ConcurrencyMode.Multiple)] public class MultipleService : ISimple { public void Put() { //Console.WriteLine("put got " + i); return; } } public class ThreadParms { public int ManagedThreadId { get; set; } public ServiceEndpoint ServiceEndpoint { get; set; } } public class BenchmarkService { public readonly int ThreadCount; public readonly int ThreadCallCount; public readonly Type ServiceType; int _completed = 0; System.Diagnostics.Stopwatch _stopWatch; EventWaitHandle _waitHandle; bool _done; public BenchmarkService(Type serviceType, int threadCount, int threadCallCount) { this.ServiceType = serviceType; this.ThreadCount = threadCount; this.ThreadCallCount = threadCallCount; _done = false; } public void Run(string baseAddress) { if (_done) throw new InvalidOperationException("Can't run twice"); ServiceHost host = new ServiceHost(ServiceType, new Uri(baseAddress)); host.Open(); Console.WriteLine("Starting " + ServiceType.Name + " on " + baseAddress + "..."); _waitHandle = new EventWaitHandle(false, EventResetMode.ManualReset); _completed = 0; _stopWatch = System.Diagnostics.Stopwatch.StartNew(); ServiceEndpoint endpoint = host.Description.Endpoints.Find(typeof(ISimple)); for (int i = 1; i <= ThreadCount; i++) { // ServiceEndpoint is NOT thread safe. Make a copy for each thread. ServiceEndpoint temp = new ServiceEndpoint(endpoint.Contract, endpoint.Binding, endpoint.Address); ThreadPool.QueueUserWorkItem(new WaitCallback(CallServiceManyTimes), new ThreadParms() { ManagedThreadId = i, ServiceEndpoint = temp }); } _waitHandle.WaitOne(); host.Shutdown(); _done = true; //Console.WriteLine("All DONE."); Console.WriteLine(" Type=" + ServiceType.Name + " ThreadCount=" + ThreadCount + " ThreadCallCount=" + ThreadCallCount); Console.WriteLine(" runtime: " + _stopWatch.ElapsedTicks + " ticks " + _stopWatch.ElapsedMilliseconds + " msec"); } public void CallServiceManyTimes(object threadParams) { ThreadParms p = (ThreadParms)threadParams; ChannelFactory<ISimple> factory = new ChannelFactory<ISimple>(p.ServiceEndpoint); ISimple proxy = factory.CreateChannel(); for (int i = 1; i < ThreadCallCount; i++) { proxy.Put(); } ((ICommunicationObject)proxy).Shutdown(); factory.Shutdown(); int currentCompleted = Interlocked.Increment(ref _completed); if (currentCompleted == ThreadCount) { _stopWatch.Stop(); _waitHandle.Set(); } } } class Program { static void Main(string[] args) { BenchmarkService benchmark; int threadCount = 600; int threadCalls = 500; string baseAddress = "net.pipe://localhost/base"; for (int i = 0; i <= 4; i++) { benchmark = new BenchmarkService(typeof(SingleService), threadCount, threadCalls); benchmark.Run(baseAddress); benchmark = new BenchmarkService(typeof(MultipleService), threadCount, threadCalls); benchmark.Run(baseAddress); } baseAddress = "http://localhost/base"; for (int i = 0; i <= 4; i++) { benchmark = new BenchmarkService(typeof(SingleService), threadCount, threadCalls); benchmark.Run(baseAddress); benchmark = new BenchmarkService(typeof(MultipleService), threadCount, threadCalls); benchmark.Run(baseAddress); } Console.WriteLine("Press ENTER to close."); Console.ReadLine(); } } public static class Extensions { static public void Shutdown(this ICommunicationObject obj) { try { if (obj != null) obj.Close(); } catch (Exception ex) { Console.WriteLine("Shutdown exception: {0}", ex.Message); obj.Abort(); } } }} 这篇关于当InstanceContextMode为具有Net.Tcp绑定的WCF服务的PerCall时,Multiple的ConcurrencyMode是否具有相关性?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持! 09-25 19:06