惯性聚合 高效追踪和阅读你感兴趣的博客、新闻、科技资讯
阅读原文 在惯性聚合中打开

推荐订阅源

H
Help Net Security
博客园 - Franky
GbyAI
GbyAI
Threat Intelligence Blog | Flashpoint
Threat Intelligence Blog | Flashpoint
爱范儿
爱范儿
IT之家
IT之家
酷 壳 – CoolShell
酷 壳 – CoolShell
aimingoo的专栏
aimingoo的专栏
博客园_首页
MongoDB | Blog
MongoDB | Blog
CTFtime.org: upcoming CTF events
CTFtime.org: upcoming CTF events
Recent Announcements
Recent Announcements
Scott Helme
Scott Helme
有赞技术团队
有赞技术团队
M
MIT News - Artificial intelligence
C
CERT Recently Published Vulnerability Notes
K
KPMG report finds enterprise disconnect between AI and its ROI | CIO
Jina AI
Jina AI
F
Fortinet All Blogs
N
Netflix TechBlog - Medium
L
LangChain Blog
L
LINUX DO - 最新话题
OSCHINA 社区最新新闻
OSCHINA 社区最新新闻
cs.AI updates on arXiv.org
cs.AI updates on arXiv.org
H
Hacker News: Front Page
MyScale Blog
MyScale Blog
P
Palo Alto Networks Blog
G
Google Developers Blog
Google DeepMind News
Google DeepMind News
AI
AI
T
Troy Hunt's Blog
Microsoft Azure Blog
Microsoft Azure Blog
阮一峰的网络日志
阮一峰的网络日志
cs.CL updates on arXiv.org
cs.CL updates on arXiv.org
Vercel News
Vercel News
Microsoft Security Blog
Microsoft Security Blog
罗磊的独立博客
S
Secure Thoughts
大猫的无限游戏
大猫的无限游戏
博客园 - 叶小钗
人人都是产品经理
人人都是产品经理
Blog — PlanetScale
Blog — PlanetScale
博客园 - 司徒正美
Apple Machine Learning Research
Apple Machine Learning Research
钛媒体:引领未来商业与生活新知
钛媒体:引领未来商业与生活新知
博客园 - 三生石上(FineUI控件)
S
Security @ Cisco Blogs
Cloudbric
Cloudbric
E
Exploit-DB.com RSS Feed
Attack and Defense Labs
Attack and Defense Labs

博客园 - 妖居

ASP.NET MVC Tips #2 - 令人混乱的Get、Post、Return View和Return Redirect ASP.NET MVC Tips #1 - 支持上传文件的ModelBinder How to migrate MsSql database to MySql Windows Workflow Foundation 使用小例 字节数组、数值和十六进制字符串的转换 表格化固定长、CSV文件编辑器工具 iMatrixitor 发布 Getting Started With LINQ in Visual Basic (翻译 + 评论) 使用接口实现附带插件功能的程序 两个简单方法加速DataGridView 使用.NET自带的功能制作简单的注册码 不是说“Peek 不会更改 StreamReader 的当前位置”么。MS骗人的! 《Introducing Visual Basic 2005》中看到的一些VB2005的新特性 VB.NET函数的返回值问题(从CSDN论坛一个问题想到的) Add-in and Automation Development In VB.NET 2003 (Finished) Add-in and Automation Development In VB.NET 2003 (8) 模拟IE地址栏的TextBox小控件 Add-in and Automation Development in VB.NET 2003 (6-7) 在WinXP环境下显示XP风格的控件 Add-in and Automation Development In VB.NET 2003 (5)
使用异步委托解决Windows Application应用Duplex Service时出现的Deadlock问题
妖居 · 2007-06-21 · via 博客园 - 妖居

Artech我的WCF之旅(6)文章中向我们介绍了Windows Application在使用Duplex Service的时候,会出现Deadlock。并且Artech还想我们介绍了两种解决办法:使用IsOneWay修饰Service Contract Operator;在Client端使用新的线程调用Service的Method。
本文继续考虑这个问题的解决方法,在Service和Client端使用异步委托。

我们模拟一个Service来完成字符串反转的操作。
Contract如下定义。

    [ServiceContract(CallbackContract = typeof(IStringBuilderCallBack))]
    
public interface IStringBuilder
    
{
        [OperationContract(IsOneWay 
= false)]
        
void Reverse(string str);
    }


    [ServiceContract]
    
public interface IStringBuilderCallBack
    
{
        [OperationContract(IsOneWay 
= false)]
        
void ShowReerseResult(string src, string dest);
    }

Service实现这个Contract。

    [ServiceBehavior(ConcurrencyMode = ConcurrencyMode.Multiple)]
    
public class StringBuilderService : IStringBuilder
    
{
        
IStringBuilder Members

    }

 Client的调用。Client Form直接实现了Callback接口。

    public partial class frmClient : Form, IStringBuilderCallback
    
{
        
private IStringBuilder m_StringBuilderProxy = null;

        
public frmClient()
        
{
            InitializeComponent();

            DuplexChannelFactory
<IStringBuilder> stringbuilderFactory =
                
new DuplexChannelFactory<IStringBuilder>(new InstanceContext(this), "defualtEndpoint");
            m_StringBuilderProxy 
= stringbuilderFactory.CreateChannel();

            ShowInfo(
"The channel is created.");
        }


        
private void btnReserve_Click(object sender, EventArgs e)
        
{
            
try
            
{
                m_StringBuilderProxy.Reverse(textBox4.Text);

                ShowInfo(
"Reserve method from StringBuilder was invoked successful.");
            }

            
catch (Exception ex)
            
{
                ShowError(ex);
            }

        }


        
IStringBuilderCallback Members
    }

这样的结果就是Deadlock。
通过Debug我们发现,实际上死锁的语句是Service上面的

callback.ShowReerseResult(str, dest);

这一行。那么我们就把这个调用变成异步委托调用,释放掉当前Thread的资源,解决死锁。
我们在StringBuilderService类里面定义如下的委托。

        private delegate void ReserveHandler(string src, string dest);

然后加入委托函数和回调函数。

        private void OnReverse(string src, string dest)
        
{
            callback.ShowReerseResult(src, dest);
        }


        
private void EndAsync(IAsyncResult ar)
        
{
            ReserveHandler r 
= null;
            
try
            
{
                System.Runtime.Remoting.Messaging.AsyncResult asres 
= (System.Runtime.Remoting.Messaging.AsyncResult)ar;
                r 
= ((ReserveHandler)asres.AsyncDelegate);
                r.EndInvoke(ar);
            }

            
catch (Exception ex)
            
{
                Console.WriteLine(ex.ToString());
            }

        }

最后修改Reverse方法。

        public void Reverse(string str)
        
{
            Console.WriteLine(
"Reserve method is invoked by client.");

            StringBuilder sb 
= new StringBuilder();
            
for (int i = str.Length - 1; i >= 0; i--)
                sb.Append(str[i]);
            
string dest = sb.ToString();

            callback 
= OperationContext.Current.GetCallbackChannel<IStringBuilderCallBack>();

            ReserveHandler rh 
= new ReserveHandler(OnReverse);
            rh.BeginInvoke(str, dest, 
new AsyncCallback(EndAsync), null);

            Console.WriteLine(
"Invoke Reserve finished.");
        }

不再直接调用callback.ShowReerseResult(src, dest)了,而是通过BeginInvoke OnReverse函数来实现异步调用。

但是这样做的结果就是Service的代码比较凌乱,如果很多的Contract都需要Callback的话,就会出现很多的Delegate和回调函数。而且Service的Contract应该是独立的,他应该只负责这个Service应该做什么,返回什么结果,而不应该把Client的一些特性加进去。因为如果我们另外一个Console Application也调用这个Contract的话,就不需要这样的操作了。
而且,在Service端使用异步委托会不会造成线程安全的问题我还没有仔细考虑过。但是就我个人对SOA的理解,上面的理由就足够让我们不要再Service里面解决Deadlock问题。
在服务器端,我们异步委托了对Client的Callback操作。那么在Client端,我们就要异步委托对Service的调用,就是要把Reverse变成异步调用的Method。

首先一个前提,我们的Client不能和Serivce共用一个Contract的Reference。因为我们要在Client端重新对Contract进行包装,让他支持异步调用。简单的办法,我们通过svcutil.exe这个工具自动生成一个Contract的定义文件(例如StringBuilderService.cs),然后加入到Client的Project里面。Client Project只引用System.ServiceModel。
修改Client端的StringBuilderService.cs文件中关于Reverse的定义部分。
原先的定义是

[System.CodeDom.Compiler.GeneratedCodeAttribute("System.ServiceModel""3.0.0.0")]
[System.ServiceModel.ServiceContractAttribute(ConfigurationName
="IStringBuilder", CallbackContract=typeof(IStringBuilderCallback))]
public interface IStringBuilder
{
    [System.ServiceModel.OperationContractAttribute(Action
="http://tempuri.org/IStringBuilder/Reverse", ReplyAction="http://tempuri.org/IStringBuilder/ReverseResponse")]
    
void Reverse(string str);
}

这是一个普通的函数,Action属性指向了这个Method将会对应到Service上面的哪一个Method实现,这里是http://tempuri.org/IStringBuilder/Reverse。
现在我们要把这一个Method变成两个,分别是BeginReverse和EndReverse。然后修改上面的Attribute。

    [System.ServiceModel.OperationContractAttribute(AsyncPattern = true, Action = "http://tempuri.org/IStringBuilder/Reverse", ReplyAction = "http://tempuri.org/IStringBuilder/ReverseResponse")]
    System.IAsyncResult BeginReverse(
string str, System.AsyncCallback callback, object asyncStat);

    
void EndReverse(System.IAsyncResult ar);

注意BeginReverse的属性里面加了一个AsyncPattern = true,他表示这个方法要使用异步调用的方式执行。同时参数也作了相应的调整。
而且void EndReverse(System.IAsyncResult ar);方法是必须的,如果没有声明那么会出现运行时错误。
最后修改我们的Client调用。

        private void btnReserve_Click(object sender, EventArgs e)
        
{
            
try
            
{
                IAsyncResult iar 
= m_StringBuilderProxy.BeginReverse(textBox4.Text, (new AsyncCallback(OnEndReserve)), null);

                ShowInfo(
"Reserve method from StringBuilder was invoked successful.");
            }

            
catch (Exception ex)
            
{
                ShowError(ex);
            }

        }


        
private void OnEndReserve(IAsyncResult ar)
        
{
            m_StringBuilderProxy.EndReverse(ar);
        }

由于我们的Reverse没有返回值,所以在OnEndReserve函数里面没有过多的操作。如果Reverse需要返回值的话,就在声明EndReserve函数的时候加入返回值即可。
这样就在Client段实现了对Service Contract Method的异步调用。
但是要注意,如果Contract有什么变化,必须要再通过svcutil.exe重新生成Client对应的Conrtact声明。不过刚才的修改就会被覆盖掉的。

程序的代码由此下载
参考资料:
[原创]我的WCF之旅(6):在Winform Application中调用Duplex Service出现TimeoutException的原因和解决方案
通过实例分析WCF Duplex消息交换