WCF后续之旅(12): 线程关联性(Thread Affinity)对WCF并发访问的影响
在本系列的html" target=_blank>上一篇文章中,我们重点讨论了线程关联性对service和callback的操作执行的影响:在service host的时候,可以设置当前线程的SynchronizationContext,那么在默认情况下,service操作的执行将在该SynchronizationContext下执行(也就将service操作包装成delegate传入SynchronizationContext的Send或者Post方法);同理,对于Duplex同行方式来讲,在client调用service之前,如果设置了当前线程的SynchronizationContext,callback操作也将自动在该SynchronizationContext下执行。
对于Windows Form Application来讲,由于UI Control的操作执行只能在control被创建的线程中易做图作,所以一这样的方式实现了自己的SynchronizationContext(WindowsFormsSynchronizationContext):将所有的操作Marshal到UI线程中。正因为如此,当我们通过Windows Form Application进行WCF service的host的时候,将会对service的并发执行带来非常大的影响。
详细讲,由于WindowsFormsSynchronizationContext的Post或者Send方法,会将目标方法的执行传到UI主线程,所以可以说,所有的service操作都在同一个线程下执行,如果有多个client的请求同时抵达,他们并不能像我们希望的那样并发的执行,而只能逐个以串行的方式执行
一、通过实例证明线程关联性对并发的影响
我们可以通过一个简单的例子证明:在默认的情况下,当我们通过Windows Form Application进行service host的时候,service的操作都是在同一个线程中执行的。我们照例创建如下的四层结构的WCF service应用:
1、Contract:IService
1: namespace Artech.ThreadAffinity2.Contracts
2: {
3: [ServiceContract]
4: public inte易做图ce 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: }为了演示对并发操作的影响,在DoSomething()中,我将线程休眠10s以模拟一个相对长时间的操作执行;为了能够直观地显示操作执行的线程和执行完成的时间,我将他们都打印在host该service的Windows Form的ListBox中,该ListBox通过static property的方式在host的时候指定。并将对ListBox的操作通过UI线程的SynchronizationContext(也是通过static property的方式在host的时候指定)的Post中执行(实际上,在默认的配置下,不需要如此,因为service操作的执行始终在Host service的UI线程下)。
3、Hosting
我们将service 的host放在一个Windows Form Application的某个一个Form的Load事件中。该Form仅仅具有一个ListBox:
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:在HostForm_Load,先在ListBox中显示当前线程的ID,然后通过Service.DispalyPanel和S
补充:综合编程 , 其他综合 ,