2012-09-01 17 views
10

Tôi luôn luôn nghĩ rằng thiết lập InstanceContextMode để PerCall làm cho chế độ đồng thời không liên quan ngay cả khi sử dụng một ràng buộc nhận thức phiên như net.tcp. Đây là những gì MSDN nói http://msdn.microsoft.com/en-us/library/ms731193.aspx "Trong PerCallinstancing, đồng thời không liên quan, bởi vì mỗi thư được xử lý bởi một InstanceContext mới và, do đó, không bao giờ nhiều hơn một chủ đề đang hoạt động trong InstanceContext."ConcurrencyMode của nhiều có liên quan khi InstanceContextMode là PerCall cho một dịch vụ WCF với Net.Tcp ràng buộc?


Nhưng hôm nay tôi đang trải qua Programming cuốn sách dịch vụ WCF Juval Lowy và ông viết trong Chương 8

Nếu dịch vụ cho mỗi cuộc gọi có một phiên giao cấp, cho dù xử lý đồng thời các cuộc gọi được phép là một sản phẩm của dịch vụ chế độ đồng thời. Nếu dịch vụ được cấu hình với ConcurrencyMode.Single, việc xử lý đồng thời các cuộc gọi đang chờ xử lý đang chờ xử lý đang chờ xử lý sẽ không được thực hiện và các cuộc gọi được gửi đi cùng một lúc. [...] Tôi coi đây là một thiết kế thiếu sót. Nếu dịch vụ là được định cấu hình với ConcurrencyMode.Multiple, đồng thời xử lý được cho phép. Các cuộc gọi được gửi đi khi chúng đến, mỗi cuộc gọi đến một cá thể mới, và thực thi đồng thời. Một quan sát thú vị ở đây là trong sự quan tâm của việc thông qua, bạn nên định cấu hình dịch vụ cho mỗi cuộc gọi với ConcurrencyMode.Multiple— bản sao sẽ vẫn an toàn cho chủ đề (vì vậy bạn sẽ không phải chịu đồng bộ hóa trách nhiệm), nhưng bạn sẽ cho phép các cuộc gọi đồng thời từ cùng một máy khách.


này mâu thuẫn với sự hiểu biết của tôi và những gì MSDN nói. Đó là chính xác? Trong trường hợp của tôi, tôi có dịch vụ WCF Net.Tcp sử dụng nhiều ứng dụng khách của mình để tạo một đối tượng proxy mới, thực hiện cuộc gọi và sau đó đóng ngay lập tức proxy. Dịch vụ này có PerCall InstanceContextMode. Tôi sẽ nhận được thông lượng được cải thiện nếu tôi thay đổi InstanceContextMode thành nhiều mà không có hành vi an toàn chủ đề tồi tệ hơn so với percall?

+0

Câu hỏi hay. Bạn đã cân nhắc việc xây dựng ứng dụng này trong ứng dụng giao diện điều khiển và thử nghiệm nó để xem? –

Trả lời

8

Cụm từ khóa trong việc đọc tuyên bố của Lowy là “vì lợi ích của thông lượng”. Lowy đang chỉ ra rằng khi sử dụng ConcurrencyMode.Single WCF sẽ thực hiện một cách mù quáng một khóa để thực thi tuần tự hóa đối với cá thể dịch vụ. Ổ khóa đắt tiền và điều này là không cần thiết vì PerCall đã đảm bảo rằng luồng thứ hai sẽ không bao giờ cố gắng gọi cùng một cá thể dịch vụ.

Về mặt hành vi: Đồng thờiMã không quan trọng đối với trường hợp dịch vụ PerCall.

Xét về hiệu suất: dịch vụ Một PerCall đó là ConcurrencyMode.Multiple nên hơi nhanh hơn bởi vì nó không tạo ra và mua lại (không cần thiết) khóa chủ đề đó ConcurrencyMode.Single đang sử dụng.

Tôi đã viết chương trình điểm chuẩn nhanh để xem liệu tôi có thể đo lường tác động hiệu suất của Đơn so với nhiều cho dịch vụ PerCall: Điểm chuẩn không có sự khác biệt có ý nghĩa.

Tôi dán vào mã bên dưới nếu bạn muốn tự mình chạy.

trường hợp thử nghiệm tôi đã cố gắng:

  • 600 chủ đề kêu gọi một dịch vụ 500 lần
  • 200 chủ đề kêu gọi một dịch vụ 1000 lần
  • 8 chủ đề kêu gọi một dịch vụ 10000 lần
  • 1 thread gọi một dịch vụ 10000 lần

Tôi chạy nó trên một CPU 4 CPU chạy dịch vụ 2008 R2. Tất cả trừ trường hợp 1 luồng là CPU bị ràng buộc.

Kết quả: Tất cả các lần chạy đều nằm trong khoảng 5% của mỗi khác. Đôi khi ConcurrencyMode.Multiple nhanh hơn. Đôi khi ConcurrencyMode.Single nhanh hơn. Có lẽ một phân tích thống kê thích hợp có thể chọn một người chiến thắng. Theo tôi, chúng đủ gần để không quan trọng.

Dưới đây là một đầu ra tiêu biểu:

Bắt đầu Độc Dịch vụ trên net.pipe: // localhost/cơ sở ... Loại = SingleService THREADCOUNT = 600 = 500 ThreadCallCount runtime: 45.156.759 ve 12.615 msec

Bắt đầu Nhiều Dịch vụ trên net.pipe: // localhost/cơ sở ... Loại = MultipleService THREADCOUNT = 600 = 500 ThreadCallCount r untime: 48731273 ticks 13.613 msec

Bắt đầu Độc Dịch vụ trên net.pipe: // localhost/cơ sở ... Loại = SingleService THREADCOUNT = 600 = 500 ThreadCallCount runtime: 48.701.509 ve 13.605 msec

Bắt đầu Nhiều Dịch vụ trên net.pipe: // localhost/cơ sở ... Loại = MultipleService THREADCOUNT = 600 = 500 ThreadCallCount runtime: 48.590.336 ve 13.574 msec

Benchmark Code:

báo trước thông thường: Đây là mã chuẩn mà có vết cắt ngắn mà không phải là thích hợp cho việc sử dụng sản xuất.

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

Cảm ơn loại xác nhận suy nghĩ của tôi. Ít nhất không có hại gì khi đặt ConcurrencyMode thành Multiple. – softveda

Các vấn đề liên quan