回调的线程关联性Word文档格式.docx
- 文档编号:19858982
- 上传时间:2023-01-11
- 格式:DOCX
- 页数:13
- 大小:20.14KB
回调的线程关联性Word文档格式.docx
《回调的线程关联性Word文档格式.docx》由会员分享,可在线阅读,更多相关《回调的线程关联性Word文档格式.docx(13页珍藏版)》请在冰豆网上搜索。
amp;
ICalculateCallback
1:
namespaceArtech.ThreadAffinity.Contracts
2:
{
3:
[ServiceContract(CallbackContract=typeof(ICalculateCallback))]
4:
publicinterfaceICalculate
5:
6:
[OperationContract]
7:
voidAdd(doubleop1,doubleop2);
8:
}
9:
这是ServiceContract,下面是CallbackContract,用于显示运算结果:
publicinterfaceICalculateCallback
voidDisplayResult(doubleresult);
2、Service:
CalculateService
namespaceArtech.ThreadAffinity.Services
[ServiceBehavior(ConcurrencyMode=ConcurrencyMode.Reentrant)]
publicclassCalculateService:
ICalculate
publicstaticListBoxDisplayPanel
{get;
set;
}
#regionICalculateMembers
10:
11:
publicvoidAdd(doubleop1,doubleop2)
12:
13:
doubleresult=op1+op2;
14:
ICalculateCallbackcallback=OperationContext.Current.GetCallbackChannel&
lt;
ICalculateCallback&
gt;
();
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是通过一个WindowsFormapplication进行host的。
并且有一个ListBox列出所有service执行的结果,就像这样:
3、Hosting
Hosting的代码写在Form的Load事件中:
privatevoidHostForm_Load(objectsender,EventArgse)
{
this._serviceHost=newServiceHost(typeof(CalculateService));
CalculateService.DisplayPanel=this.listBoxResult;
CalculateService.SynchronizationContext=SynchronizationContext.Current;
this._serviceHost.Opened+=delegate
this.Text="
Thecalculateservicehasbeenstartedup!
"
;
};
this._serviceHost.Open();
我们注意到了CalculateService使用到的用于显示所有预算结果的ListBox就是在这了通过staticproperty传递的。
这么配置文件
&
configuration&
system.serviceModel&
services&
servicename="
Artech.ThreadAffinity.Services.CalculateService"
&
endpointbinding="
netTcpBinding"
bindingConfiguration="
contract="
Artech.ThreadAffinity.Contracts.ICalculate"
/&
host&
baseAddresses&
addbaseAddress="
net.tcp:
//127.0.0.1:
8888/calculateservice"
/baseAddresses&
/host&
/service&
/services&
/system.serviceModel&
/configuration&
4、Client
Client的界面很简单:
输入两个操作数,点击“=”按钮,将运算结果显示出来。
先来看看client端对callbackcontract的实现:
namespaceClients
publicclassCalculateCallback:
publicstaticTextBoxResultPanel;
#regionICalculateCallbackMembers
publicvoidDisplayResult(doubleresult)
ResultPanel.Text=result.ToString();
这是配置:
client&
endpointaddress="
binding="
name="
calculateservice"
/client&
然后是我们“=”按钮的单击事件对运算的实现:
privatevoidbuttonCalculate_Click(objectsender,EventArgse)
CalculateCallback.ResultPanel=this.textBoxResult;
DuplexChannelFactory&
ICalculate&
channelFactory=newDuplexChannelFactory&
(newCalculateCallback(),"
);
ICalculatecalculator=channelFactory.CreateChannel();
calculator.Add(double.Parse(this.textBoxOp1.Text),double.Parse(this.textBoxOp2.Text));
CalculateCallback用于显示运算结果的TextBox通过statisproperty实现传递。
这个实现很简单,貌似没有什么问题,但是我们运行程序,在客户端就会抛出这样的exception。
可以看出是一个TimeoutException。
二、是什么导致TimeoutException?
我们现在来分析是什么导致了TimeoutException的抛出。
原因很简单:
由于我们对service的调用的是在UI线程调用的,所以在开始调用到最终得到结果,这个UIThread会被锁住;
但是当service进行了相应的运算的到运算的结果后,需要调用callback对象对client进行回调,默认的情况下,Callback的执行是在UI线程执行的。
当Callback试图执行的时候,发现UI线程被锁,只能等待。
这样形成一个死锁,UI线程需要等待CalculateService执行返回后才能解锁,而CalculateService需要Callback执行完成;
而Callback需要等到UI线程解锁才能执行。
基于上门的原因,我们有两种解决方案:
·
CalculateService不必等到Callback执行完成就返回,我们可以通过异步调用Callback。
或者让Client异步方式调用CalculateService,以便及时释放UI线程,我们可以通过One-way的方式来进行service的调用。
让Callback的执行不必绑定到UI线程
三、解决方案一:
通过异步调用或者One-way回调
为了简单起见,我们通过ThreadPool实现了异步回调:
ThreadPool.QueueUserWorkItem(delegate{callback.DisplayResult(result);
},null);
这是一种方案,另一种是将Add操作设成One-way的:
[OperationContract(IsOneWay=true)]
这两种方案都可以解决问题。
四、方案二、通过解除Callback操作和UI线程的关联性
现在我们才进入我们今天讨论的主题:
WCF并发操作的线程关联性问题。
在这之前,我们需要了解一个重要的对象:
SynchonizationContext(System.Threading.SynchronizationContext)。
SynchonizationContext就是为了解决这种线程关联性问题而设计的。
SynchonizationContext提供了两个主要的API将操作和对应的Thread关联:
Post和Send。
publicvirtualvoidPost(SendOrPostCallbackd,objectstate)
publicvirtualvoidSend(SendOrPostCallbackd,objectstate)
Send和Post分别以同步和异步的方式将以Delegate表示的具体的操作和SynchonizationContext对象对应的Thread关联,而SendOrPostCallbackdelegate对象代表你需要的线程关联操作,state代表传入delegate的参数:
publicdelegatevoidSendOrPostCallback(objectstate);
对于某些具有线程关联的应用,比如WindowsFormapplication,在程序启动的时候,会设置当前的SynchonizationContext对象(WindowsFormapplication使用的是继承了SynchonizationContext的WindowsFormsSynchronizationContext:
System.Windows.Forms.WindowsFormsSynchronizationContext)。
当前SynchonizationContext被成功初始化后,你就可以通过SynchonizationContext的静态属性Current得到它。
在你自己的应用中,如何有需要,你也可以自定义SynchonizationContext,并通过静态方法SetSynchronizationContext将其设置为currentSynchronizationContext。
对应WCF来说,无论是host一个service,还是在调用service时制定callback,在默认的情况下,service和callback的操作将自动和当前的SynchonizationContext进行关联(如何有的话)。
也就是说,如过我们的service被host到WindowsFormapplication下,那么service的操作将在UI线程下执行;
同理,如何我们在一个WindowsFormsUI线程下调用duplexservice并制定callback,那么callback的最终执行将在UI线程。
关于WCF对线程关联性的控制,可以通过ServiceBehavior或者CallbackBehavior的UseSynchronizationContext属性进行设定,该属性默认为true,这正式WCF默认具有线程关联性的原因。
现在我们来实现我们的第二套方案:
让Callback的执行不必绑定到UI线程。
为此我们只需要加上如何的CallbackBehaviorattribute就可以了。
namespaceArtech.ThreadAffinity.Clients
[CallbackBehavior(UseSynchronizationContext=false)]
但是现在我们运行我们的程序,将会出现如下的InvalidOperation异常:
原因很简单,由于我们将callbaclk的UseSynchronizationContext设置成false,那么callback的操作将不会再UI线程下执行。
但是我们需要运算的结果输入到UI的TextBox上,对UI上控件的操作需要在UI线程上执行,显然会抛出异常了。
为了我们引入SynchonizationContext到CalculateCallback中:
将SynchonizationContext定义成一个static属性,通过Post方法异步地实现对运算结果的显示。
publicstaticSynchronizationContextSynchronizationContext;
SynchronizationContext.Post(delegate{ResultPanel.Text=result.ToString();
SynchonizationContext在调用service的时候指定:
CalculateCallback.SynchronizationContext=SynchronizationContext.Current;
现在我们程序能够正常运行了。
五、另一种可选方案:
通过ISynchronizeInvoke的Invoke/BeginInvoke
熟悉WindowsForm编程的读者应该都知道,WinForm空间的基类Control(System.Windows.Forms.Control)都实现了System.ComponentModel.ISynchronizeInvoke接口,而Control对ISynchronizeInvoke的实现就是为了解决Control的操作必须在创建Control线程的问题,ISynchronizeInvoke定义Invoke和BeginInvoke方法方面我们以同步或者异步的方式操作Control:
publicinterfaceISynchronizeInvoke
//Methods
[HostProtection(SecurityAction.LinkDemand,Synchronization=true,ExternalThreading=true)]
IAsyncResultBeginInvoke(Delegatemethod,object[]args);
objectEndInvoke(IAsyncResultresult);
objectInvoke(Delegatemethod,object[]args);
//Properties
boolInvokeRequired{get;
如何我们放弃基于SynchonizationContext的解决方案,我们也可以通过基于ISynchronizeInvoke的方式来解决这个问题。
为此我们这样定义CalculateCallback:
namespaceArtech
- 配套讲稿:
如PPT文件的首页显示word图标,表示该PPT已包含配套word讲稿。双击word图标可打开word文档。
- 特殊限制:
部分文档作品中含有的国旗、国徽等图片,仅作为作品整体效果示例展示,禁止商用。设计者仅对作品中独创性部分享有著作权。
- 关 键 词:
- 线程 关联性
![提示](https://static.bdocx.com/images/bang_tan.gif)