閱讀464 返回首頁    go 阿裏雲 go 技術社區[雲棲]


[原創]WCF後續之旅(12): 線程關聯性(Thread Affinity)對WCF並發訪問的影響

在本係列的上一篇文章中,我們重點討論了線程關聯性對service和callback的操作執行的影響:在service host的時候,可以設置當前線程的SynchronizationContext,那麼在默認情況下,service操作的執行將在該SynchronizationContext下執行(也就將service操作包裝成delegate傳入SynchronizationContext的Send或者Post方法);同理,對於Duplex同行方式來講,在client調用service之前,如果設置了當前線程的SynchronizationContext,callback操作也將自動在該SynchronizationContext下執行。

詳細講,由於WindowsFormsSynchronizationContext的Post或者Send方法,會將目標方法的執行傳到UI主線程,所以可以說,所有的service操作都在同一個線程下執行,如果有多個client的請求同時抵達,他們並不能像我們希望的那樣並發的執行,而隻能逐個以串行的方式執行。(Source Code從這裏下載)

一、通過實例證明線程關聯性對並發的影響

image

   1: namespace Artech.ThreadAffinity2.Contracts
   2: {
   3:     [ServiceContract]
   4:     public interface IService
   5:     {
   6:         [OperationContract]
   7:         void DoSomething();
   8:     }
   9: }
2、Service:Service

   1: namespace Artech.ThreadAffinity2.Services
   2: {
   3:     public class Service:IService
   4:     {
   5:         public static ListBox DispalyPanel
   6:         { get; set; } 
   7:  
   8:         public static SynchronizationContext SynchronizationContext
   9:         { get; set; } 
  10:  
  11:         #region IService Members 
  12:  
  13:         public void DoSomething()
  14:         {
  15:             Thread.Sleep(5000);
  16:             int threadID = Thread.CurrentThread.ManagedThreadId;
  17:             DateTime endTime = DateTime.Now;
  18:             SynchronizationContext.Post(delegate
  19:             {
  20:                 DispalyPanel.Items.Add(string.Format("Serice execution ended at {0}, Thread ID: {1}",
  21:                     endTime, threadID));
  22:             }, null);
  23:         } 
  24:  
  25:         #endregion
  26:     }
  27: } 

   1: namespace Artech.ThreadAffinity2.Hosting
   2: {
   3:     public partial class HostForm : Form
   4:     {
   5:         private ServiceHost _serviceHost; 
   6:  
   7:         public HostForm()
   8:         {
   9:             InitializeComponent();
  10:         } 
  11:  
  12:         private void HostForm_Load(object sender, EventArgs e)
  13:         {
  14:             this.listBoxResult.Items.Add(string.Format("The ID of the Main Thread: {0}", Thread.CurrentThread.ManagedThreadId));
  15:             this._serviceHost = new ServiceHost(typeof(Service));
  16:             this._serviceHost.Opened += delegate
  17:             { 
  18:                 this.Text = "Service has been started up!";
  19:             };
  20:             Service.DispalyPanel = this.listBoxResult;
  21:             Service.SynchronizationContext = SynchronizationContext.Current;
  22:             this._serviceHost.Open();
  23:         } 
  24:  
  25:         private void HostForm_FormClosed(object sender, FormClosedEventArgs e)
  26:         {
  27:             this._serviceHost.Close();
  28:         }
  29:     }
  30: } 
  31:  

   1: <?xml version="1.0" encoding="utf-8" ?>
   2: <configuration>
   3:     <system.serviceModel>
   4:         <services>
   5:             <service name="Artech.ThreadAffinity2.Services.Service">
   6:                 <endpoint binding="basicHttpBinding" contract="Artech.ThreadAffinity2.Contracts.IService" />
   7:                 <host>
   8:                     <baseAddresses>
   9:                         <add baseAddress="https://127.0.0.1/service" />
  10:                     </baseAddresses>
  11:                 </host>
  12:             </service>
  13:         </services>
  14:     </system.serviceModel>
  15: </configuration> 

   1: <?xml version="1.0" encoding="utf-8" ?>
   2: <configuration>
   3:     <system.serviceModel>
   4:         <client>
   5:             <endpoint address="https://127.0.0.1/service" binding="basicHttpBinding"
   6:                 contract="Artech.ThreadAffinity2.Contracts.IService" name="service" />
   7:         </client>
   8:     </system.serviceModel>
   9: </configuration>
   1: namespace Clients
   2: {
   3:     class Program
   4:     {
   5:         static void Main(string[] args)
   6:         {
   7:             using (ChannelFactory<IService> channelFactory = new ChannelFactory<IService>("service"))
   8:             {
   9:                 IList<IService> channelList = new List<IService>();
  10:                 for (int i = 0; i < 10; i++)
  11:                 {
  12:                     channelList.Add(channelFactory.CreateChannel());
  13:                 } 
  14:  
  15:                 Array.ForEach<IService>(channelList.ToArray<IService>(), 
  16:                     delegate(IService channel)
  17:                 { 
  18:                     ThreadPool.QueueUserWorkItem(
  19:                     delegate
  20:                     {
  21:                         channel.DoSomething();
  22:                         Console.WriteLine("Service invocation ended at {0}", DateTime.Now);
  23:                     }, null);
  24:                 } );
  25:                 Console.Read();
  26:             }
  27:         }
  28:     }
  29: } 
  30:  

image

image

二、解除線程的關聯性

在本係列的上一篇文章,我們介紹了service的線程關聯性通過ServiceBeahavior的控製。UseSynchronizationContext實際上代表的是是否使用預設的SynchronizationContext(實際上是DispatchRuntime的SynchronizationContext屬性中製定的)。我們對service的代碼進行如下簡單的修改,使service執行過程中不再使用預設的SynchronizationContext。

   1: namespace Artech.ThreadAffinity2.Services
   2: {
   3:     [ServiceBehavior(UseSynchronizationContext = false)]    
   4:     public class Service:IService
   5:     {
   6:  
   7:          //...
   8:     }
   9: }

image

image

結論:當我們使用Windows Form Application進行service host的時候,首先應該考慮到在默認的情況下具有線程關聯特性。你需要評估的service的整個操作是否真的需要依賴於當前UI線程,如果不需要或者隻有部分操作需要,將UseSynchronizationContext 設成false,將會提高service處理的並發量。對於依賴於當前UI線程的部分操作,可以通過SynchronizationContext實現將操作Marshal到UI線程中處理,對於這種操作,應該盡力那個縮短執行的時間。

WCF後續之旅:
WCF後續之旅(1): WCF是如何通過Binding進行通信的
WCF後續之旅(2): 如何對Channel Layer進行擴展——創建自定義Channel
WCF後續之旅(3): WCF Service Mode Layer 的中樞—Dispatcher
WCF後續之旅(4):WCF Extension Point 概覽
WCF後續之旅(5): 通過WCF Extension實現Localization
WCF後續之旅(6): 通過WCF Extension實現Context信息的傳遞
WCF後續之旅(7):通過WCF Extension實現和Enterprise Library Unity Container的集成
WCF後續之旅(8):通過WCF Extension 實現與MS Enterprise Library Policy Injection Application Block 的集成
WCF後續之旅(9):通過WCF的雙向通信實現Session管理[Part I]
WCF後續之旅(9): 通過WCF雙向通信實現Session管理[Part II]
WCF後續之旅(10): 通過WCF Extension實現以對象池的方式創建Service Instance
WCF後續之旅(11): 關於並發、回調的線程關聯性(Thread Affinity)
WCF後續之旅(12): 線程關聯性(Thread Affinity)對WCF並發訪問的影響
WCF後續之旅(13): 創建一個簡單的WCF SOAP Message攔截、轉發工具[上篇]
WCF後續之旅(13):創建一個簡單的SOAP Message攔截、轉發工具[下篇]
WCF後續之旅(14):TCP端口共享
WCF後續之旅(15): 邏輯地址和物理地址
WCF後續之旅(16): 消息是如何分發到Endpoint的--消息篩選(Message Filter)
WCF後續之旅(17):通過tcpTracer進行消息的路由


作者:蔣金楠
微信公眾賬號:大內老A
微博:www.weibo.com/artech
如果你想及時得到個人撰寫文章以及著作的消息推送,或者想看看個人推薦的技術資料,可以掃描左邊二維碼(或者長按識別二維碼)關注個人公眾號(原來公眾帳號蔣金楠的自媒體將會停用)。
本文版權歸作者和博客園共有,歡迎轉載,但未經作者同意必須保留此段聲明,且在文章頁麵明顯位置給出原文連接,否則保留追究法律責任的權利。
原文鏈接

最後更新:2017-10-30 17:04:09

  上一篇:go  在TensorFlow中對比兩大生成模型:VAE與GAN(附測試代碼)
  下一篇:go  《WCF後續之旅》博文係列總結[共17篇]