2012-09-01 11 views
10

は、私はいつもPerCallにInstanceContextModeを設定するとnet.tcpのような結合を意識セッションを使用した場合でも、同時実行モードが無関係になると思いました。これは、MSDNは http://msdn.microsoft.com/en-us/library/ms731193.aspx が言うことである「とは、各メッセージは新しいInstanceContextと、それゆえ、決して複数のスレッドがInstanceContextでアクティブになっていることで処理されるためPerCallinstancingでは、同時実行は、関係ありません。」NetTcpバインディングを持つWCFサービスのInstanceContextModeがPerCallの場合、複数のConcurrencyModeは関連性がありますか?


しかし、今日、私はジュバル・ロウィの本プログラミングWCFサービスを通じて行っていたとコールごとのサービスは、通話の 並行処理するかどうか、トランスポートレベルのセッションを持っている場合、彼は第8章

に書き込み、サービスの製品は 並行性モードです。サービスは ConcurrencyMode.Single、保留 通話の同時処理で構成されている場合はアルlowedではない、との通話は、一度に1派遣されています。 [...]私はこれが欠陥のあるデザインだと考えています。サービスが ConcurrencyMode.Multipleで構成されている場合、同時プロcessingは許さ です。コールは到着時に新しいインスタンス( )にそれぞれディスパッチされ、同時に実行されます。ここで興味深い観察は にスループットの関心は、それは良いアイデアは まだスレッドセーフになります(あなたが発生しませんConcurrencyMode.Multiple-でインスタンス自体を ごとの通話サービスを設定することであるということです同期 責任)、同じクライアントからの同時呼び出しを許可します。


これが私の理解と何MSDNが言うと矛盾しています。どちらが正しい ? 私の場合、私はWCF Net.Tcpサービスを使用して、新しいプロキシオブジェクトを作成した多くのクライアントアプリケーションを使用し、呼び出しを行い、すぐにプロキシを閉じます。このサービスにはPerCall InstanceContextModeがあります。 percallと比べてInstanceContextModeを複数の悪いスレッド安全性の動作に変更すると、スループットが向上しますか? Lowy氏の発言を読んで

+0

優秀な質問です。あなたはこれをコンソールアプリケーションで構築し、それをテストすることを検討しましたか? –

答えて

8

キーフレーズは、「スループットの関心で」です。 Lowyは、ConcurrencyMode.Single WCFを使用すると、盲目的にロックを実装してサービスインスタンスのシリアル化を強制することを指摘しています。ロックは高価であり、PerCallはすでに2番目のスレッドが決して同じサービスインスタンスを呼び出そうとしないことを保証しているので、これは必要ではありません。

動作に関して: PerCallサービスインスタンスではConcurrencyModeは重要ではありません。性能の面で

:その作成およびConcurrencyMode.Singleが使用している(不要な)スレッド・ロックを取得していないので、ConcurrencyMode.Multipleが若干速くなるべきである A PerCallサービス。

私は、PerCallサービスに対するSingle対Multipleのパフォーマンスへの影響を測定できるかどうかを確認するためのクイックベンチマークプログラムを作成しました。ベンチマークでは意味がありません。

あなた自身で実行したい場合は、以下のコードに貼り付けてください。私が試し

テストケース:1000回呼び出すサービス10000倍

  • 1スレッドを呼び出す
  • 8スレッドサービスを呼び出すサービス500倍
  • 200スレッドを呼び出す

    • 600スレッドサービス10000回

    私はこれを4 CPU VM Service 2008 R2を実行している。 1つのスレッドの場合を除いて、すべてCPUが制約されていました。

    結果: すべての実行はお互いの約5%以内であった。 時々、ConcurrencyMode.Multipleが高速でした。時にはConcurrencyMode.Singleが速かった。適切な統計分析が勝者を選ぶ可能性があります。私の意見では、彼らは問題ではないほど近くにあります。 net.pipeにシングルサービスを開始する

    :// localhost /をベース... タイプ= SingleService THREADCOUNT = 600 ThreadCallCount = 500 ランタイム:45156759ティック 12615ミリ秒

    はここで、一般的な出力です

    net.pipeに複数サービスの開始:// localhost /をベース... タイプ= MultipleService THREADCOUNT = 600 ThreadCallCount = 500 R untime:48731273ティック 13613ミリ秒

    net.pipe上シングルサービス開始:// localhost /をベース... タイプ= SingleService THREADCOUNT = 600 ThreadCallCount = 500 ランタイム:48701509ティック 13605ミリ秒

    開始複数サービスnet.pipe上:// localhost /をベース... タイプ= 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(); 
          } 
         } 
        } 
    } 
    
  • +0

    私の考えを確認してくれてありがとう。 ConcurrencyModeをMultipleに設定することは、少なくとも害はありません。 – softveda

    関連する問題