当前位置:编程学习 > 网站相关 >>

WCF后续之旅(11): 关于并发、回调的线程关联性(Thread Affinity)

对于一般的多线程操作,比如异步地进行基于文件系统的IO操作;异步地调用Web Service;或者是异步地进行数据库访问等等,是和具体的线程无关的。也就是说,对于这些操作,任意创建一个新的线程来执行都是等效的。但是有些情况下,有些操作却只能在固定的线程下执行。比如,在GUI应用下,对控件的访问就需要在创建该控件的线程下执行;或者我们在某个固定的线程中通过TLS(Thread Local Storage)设置了一些Context信息,供具体的操作使用,我们把操作和某个固定的线程的依赖称为线程关联性(Thread Affinity)。在这种情况下,我们的异步操作就需要被Marshal到固定的线程执行。在WCF并发或者Callback的情况下也具有这样的基于线程关联性的问题。

一、从基于Windows Application客户端的WCF回调失败谈起

在"我的WCF之旅"系列文章中,有一篇(html" target=_blank>WinForm Application中调用Duplex Service出现TimeoutException的原因和解决方案)专门介绍在一个Windows Application客户端应用, 通过WCF 的Duplex通信方式进行回调失败的文章.我们今天以此作为出发点介绍WCF在Thread Affinity下的表现和解决方案.

我们来创建一个WCF的应用来模拟该场景: 客户端是一个基于Windows Form应用, 完成一个计算器的功能, 用户输入操作数,点击"计算"按钮, 后台通过调用WCF service, 并传递一个用于显示计算结果的Callback对象; service进行相应的计算得到最后的运算结果,调用该Callback对象将运算结果显示到客户端界面.这是我们的WCF四层结构:

image

1、Contract:ICalculate & ICalculateCallback

 

   1: namespace Artech.ThreadAffinity.Contracts
   2: {
   3:     [ServiceContract(CallbackContract = typeof(ICalculateCallback))]
   4:     public inte易做图ce ICalculate
   5:     {
   6:         [OperationContract]
   7:         void Add(double op1, double op2);
   8:     }
   9: }

这是Service Contract,下面是Callback Contract,用于显示运算结果:

   1: namespace Artech.ThreadAffinity.Contracts
   2: {
   3:    public inte易做图ce ICalculateCallback
   4:     {
   5:         [OperationContract]
   6:         void DisplayResult(double result);
   7:     }
   8: }

2、Service:CalculateService

   1: namespace Artech.ThreadAffinity.Services
   2: {
   3:     [ServiceBehavior(ConcurrencyMode = ConcurrencyMode.Reentrant)]
   4:     public class CalculateService:ICalculate
   5:     {
   6:         public static ListBox DisplayPanel
   7:         { get; set; }        
   8: 
   9:         #region ICalculate Members
  10: 
  11:         public void Add(double op1, double op2)
  12:         {
  13:             double result = op1 + op2;
  14:             ICalculateCallback callback = OperationContext.Current.GetCallbackChannel<ICalculateCallback>();           
  15: 
  16: DisplayPanel.Items.Add(string.Format("{0} + {1} = {2}", op1, op2, result));
  17: 
  18:             callback.DisplayResult(result);
  19:         }
  20: 
  21:         #endregion
  22:     }
  23: }

 

 

由于需要进行callback, 我们把ConcurrencyMode 设为Reentrant。当得到运算的结果后,通过OperationContext.Current.GetCallbackChannel得到callback对象,并调用之。还有一点需要提的是,该service是通过一个Windows Form application进行host的。并且有一个ListBox列出所有service执行的结果,就像这样:

image

3、Hosting

Hosting的代码写在Form的Load事件中:

   1: private void HostForm_Load(object sender, EventArgs e)
   2: {   
   3:     this._serviceHost = new ServiceHost(typeof(CalculateService));
   4:     CalculateService.DisplayPanel = this.listBoxResult;
   5:     CalculateService.SynchronizationContext = SynchronizationContext.Current;
   6:     this._serviceHost.Opened += delegate
   7:     {
   8: this.Text = "The calculate service has been started up!";
   9:     };
  10: 
  11:     this._serviceHost.Open();
  12: }

我们注意到了CalculateService使用到的用于显示所有预算结果的ListBox就是在这了通过static property传递的。

这么配置文件

   1: <configuration>
   2:     <system.serviceModel>
   3:         <services>
   4:             <service name="Artech.ThreadAffinity.Services.CalculateService">
   5:                 <endpoint binding="netTcpBinding" bindingConfiguration="" contract="Artech.ThreadAffinity.Contracts.ICalculate" />
   6:                 <host>
   7:                     <baseAddresses>
   8:                         <add baseAddress="net.tcp://

补充:综合编程 , 其他综合 ,
CopyRight © 2022 站长资源库 编程知识问答 zzzyk.com All Rights Reserved
部分文章来自网络,