Warning: file_get_contents(/data/phpspider/zhask/data//catemap/6/multithreading/4.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Multithreading 当InstanceContextMode为具有Net.Tcp绑定的WCF服务的PerCall时,多个并发模式是否具有相关性?_Multithreading_Wcf - Fatal编程技术网

Multithreading 当InstanceContextMode为具有Net.Tcp绑定的WCF服务的PerCall时,多个并发模式是否具有相关性?

Multithreading 当InstanceContextMode为具有Net.Tcp绑定的WCF服务的PerCall时,多个并发模式是否具有相关性?,multithreading,wcf,Multithreading,Wcf,我一直认为,将InstanceContextMode设置为PerCall会使并发模式变得无关紧要,即使是使用诸如net.tcp之类的会话感知绑定。这就是MSDN所说的 在PercalInstance中,并发性是不相关的,因为每条消息都由一个新的InstanceContext处理,因此InstanceContext中活动的线程不会超过一个 但今天我读了Juval Lowy的书《编程WCF服务》,他在第8章中写道 如果每次呼叫服务具有传输级别会话,则 允许调用的并发处理是服务的一个产品 并发模式。

我一直认为,将InstanceContextMode设置为PerCall会使并发模式变得无关紧要,即使是使用诸如net.tcp之类的会话感知绑定。这就是MSDN所说的 在PercalInstance中,并发性是不相关的,因为每条消息都由一个新的InstanceContext处理,因此InstanceContext中活动的线程不会超过一个


但今天我读了Juval Lowy的书《编程WCF服务》,他在第8章中写道

如果每次呼叫服务具有传输级别会话,则 允许调用的并发处理是服务的一个产品 并发模式。如果服务配置为 ConcurrencyMode。对挂起的 呼叫不被分配,呼叫一次分配一个。 我认为这是一个有缺陷的设计。如果服务是 配置了ConcurrencyMode。需要多个并发处理 允许。调用在到达时被调度,每个调用都被调度到一个新实例, 并同时执行。一个有趣的观察结果是 出于对吞吐量的兴趣,配置一个 具有ConcurrencyMode.Multiple的每次调用服务-实例本身 仍将是线程安全的(因此您不会导致同步) 但您将允许来自同一客户端的并发调用


这与我的理解和MSDN的说法相矛盾。哪个是正确的?
在我的例子中,我有一个WCF Net.Tcp服务,它使用我的许多客户端应用程序创建一个新的代理对象,进行调用,然后立即关闭代理。该服务具有PerCall InstanceContextMode。如果我将InstanceContextMode更改为Multiple,并且没有比percall更糟糕的线程安全行为,那么吞吐量会提高吗

阅读Lowy陈述的关键短语是“为了吞吐量”。Lowy指出,当使用ConcurrencyMode时,单个WCF将盲目地实现一个锁来强制序列化服务实例。锁是昂贵的,并且这个锁不是必需的,因为PerCall已经保证第二个线程永远不会尝试调用同一个服务实例

在行为方面: ConcurrencyMode对于PerCall服务实例并不重要

在性能方面: 作为ConcurrencyMode.Multiple的PerCall服务应该稍微快一点,因为它不会创建和获取ConcurrencyMode.Single正在使用的(不需要的)线程锁

我编写了一个快速基准测试程序,看看是否可以衡量PerCall服务的单个与多个对性能的影响:基准测试没有显示出有意义的差异。

如果您想自己尝试运行它,我在下面粘贴了代码

我尝试过的测试用例:

  • 600个线程调用服务500次
  • 200个线程调用服务1000次
  • 8个线程调用一个服务10000次
  • 1个线程调用服务10000次
我在运行Service2008R2的4CPU虚拟机上运行了这个。除了1个线程外,其他所有线程都受到CPU限制

结果: 所有跑步距离都在5%以内。 有时并发模式。多个更快。有时是并发模式。单人模式更快。也许一个适当的统计分析可以选出一个赢家。在我看来,它们已经足够接近了,不重要了

以下是一个典型的输出:

在网上启动单一服务。pipe://localhost/base... 类型=SingleService ThreadCount=600 ThreadCallCount=500 运行时:45156759个滴答声12615毫秒

在网上启动多个
服务。pipe://localhost/base... 类型=MultipleService ThreadCount=600 ThreadCallCount=500 运行时:48731273个滴答声13613毫秒

在网上启动单一服务。pipe://localhost/base... 类型=SingleService ThreadCount=600 ThreadCallCount=500 运行时:48701509个滴答声13605毫秒

在网上启动多个
服务。pipe://localhost/base... 类型=MultipleService 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();
            }
        }
    }
}
使用系统;
使用System.Collections.Generic;
使用System.Linq;
使用System.ServiceModel;
使用System.ServiceModel.Description;
使用系统文本;
使用系统线程;
使用System.Threading.Tasks;
名称空间WCFTest
{
[服务合同]
公共接口很简单
{
[运营合同()]
无效看跌期权();
}
[ServiceBehavior(InstanceContextMode=InstanceContextMode.PerCall,ConcurrencyMode=ConcurrencyMode.Single)]
公共类单服务:ISimple
{
公开宣布作废
{
//Console.WriteLine(“put-get”+i);
返回;
}
}
[ServiceBehavior(InstanceContextMode=InstanceContextMode.PerCall,ConcurrencyMode=ConcurrencyMode.Multiple)]
公共类多重服务:ISimple
{
公开宣布作废
{
//Console.WriteLine(“put-get”+i);
返回;
}
}
公共类线程参数
{
public int-ManagedThreadId{get;set;}
公共ServiceEndpoint ServiceEndpoint{get;set;}
}
公共类基准服务
{
公共只读int ThreadCount;
公共只读int-ThreadCallCount;
公共只读类型ServiceType;
int _completed=0;
System.Diagnostics.Stopwatch\u秒表;
EventWaitHandle\u waitHandle;
完成了;
公共基准测试服务(类型serviceType,int-threadC