[原創]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從這裏下載)
一、通過實例證明線程關聯性對並發的影響
1: namespace Artech.ThreadAffinity2.Contracts
2: {
3: [ServiceContract]
4: public interface IService
5: {
6: [OperationContract]
7: void DoSomething();
8: }
9: }
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:
二、解除線程的關聯性
在本係列的上一篇文章,我們介紹了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: }
結論:當我們使用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