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

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

原文地址:https://www.cnblogs.com/Winston/p/1346910.html