UI thread client callback和UI thread WCF Service一起工作时死锁的形成原因及解决方法

前言:

当WCF service和WCF client都是基于UI时,当服务器端callback客户端时,此时若想对于Service端或者Client端的UI进行update,例如某个控件的状态的改变,需要进行严格的同步管理,否则Service|Client UI thread就会陷入deadlock。

 

deaklock形成原因:

假设有以下一个scenario:

1个windows forms client已经建立了UI的同步上下文信息(UI SynchronizationContext)和回调对象(callback object)之间的姻联。然后该基于UI的客户端调用一个WCF service,连同其callback reference一起传送至service。 假设WCF service的ConcurrencyMode配置为ReEntrant。那么service在收到这个request之后开始callback 客户端。这就形成了一个死锁!因为对客户端的回调请求是执行在客户端的UI thread中的,而此时该UI thread在等待刚刚发出去的service call的结果。

有人可能想:将callback operation的MEP(Message Exchange Pattern)配置为IsOneWay可以吗? 还是不行。因为service对client 的callback request还是首先需要被marshaled到client UI thread.

题外话:如果将service operation和callback operation都设置为IsOneWay可以解决deadlock吗? 您自己去想吧:)

解决方法:

解决方法是将service端对client的callback请求设置成异步地(asynchronously)marshal到client UI thread即可

例如:服务器端operation 请求callback的正确代码如下:

 1 public string DoSomething(int value)
 2         {
 3             MyForm form = System.Windows.Forms.Application.OpenForms[0as MyForm;
 4             SendOrPostCallback del = delegate
 5             {
 6                 form.ServiceLable = callback.OnCallBack();
 7             };
 8 
 9             System.Diagnostics.Debug.Assert(form.MySynchronizationContext != null);
10             form.MySynchronizationContext.Post(del, null);
//form.MySynchronizationContext.Send(del,null)就会死锁!!
11 
12             return "The string is returned from WCF service";
13         }

 

 source code:

1. service contract and callback contract

 

Code

 

client UI source code:

using  System;
using  System.Collections.Generic;
using  System.ComponentModel;
using  System.Data;
using  System.Drawing;
using  System.Linq;
using  System.Text;
using  System.Windows.Forms;
using  System.ServiceModel;
using  System.Threading;

namespace  UICallBack_DeadLock_Client
{
    
public   partial   class  CallBackForm : Form,IMyContractCallback
    {
        
private  MyContractClient proxy;
        
private  SynchronizationContext m_ClientCallBackSynchronizationContext;
        
private   string  returnValue  =  String.Empty;

        
public  CallBackForm()
        {
            InitializeComponent();
            m_ClientCallBackSynchronizationContext 
=  SynchronizationContext.Current;
        }

        
public   string  OnCallBack()
        {
            SendOrPostCallback setText 
=   delegate
            {
                m_ClientLabel.Text 
=  returnValue;
            };

            DialogResult result 
=  MessageBox.Show( " please select one answer " " callback test " , MessageBoxButtons.YesNo, MessageBoxIcon.Question);
            m_ClientCallBackSynchronizationContext.Post(setText, 
null );

            
return  result == DialogResult.Yes ? " client answer via Callback is YES " : " client answer via Callback is NO " ;
        }

        
private   void  CallBackForm_Load( object  sender, EventArgs e)
        {

        }

        
private   void  button1_Click( object  sender, EventArgs e)
        {
            InstanceContext instanceContext 
=   new  InstanceContext( this );
            proxy 
=   new  MyContractClient(instanceContext);
            proxy.Connect();
            returnValue
= proxy.DoSomething( 2 );
            
        }
    }
}

运行screenshot

UI thread client callback和UI thread WCF Service一起工作时死锁的形成原因及解决方法

For all source code regarding to this article, please press here to download

你可能感兴趣的:(callback)