• <转>WCF中出现死锁或者超时


    一、服务器端死锁

    对于如下服务:

       

     [ServiceContract(CallbackContract = typeof(INotify))]
        public class DownloadService
        {
            [OperationContract]
            public void Download()
            {
                //开始下载操作
                //.....
    
                //通知下载完成
                var callback = OperationContext.Current.GetCallbackChannel<INotify>();
                callback.DownloadComplete();
            }
        }
    
        interface Inotify
        {
            [OperationContract]
            void DownloadComplete();
        }

    首先我们用一个控制台程序作为客户端,代码如下:

        

    class Program
        {
            static void Main(string[] args)
            {
                var context = new System.ServiceModel.InstanceContext(new CallBack());
                var client = new DownloadServiceClient(context);
                client.Download();
            }
        }
    
        class CallBack : DownloadServiceCallback
        {
            public void DownloadComplete()
            {
                Console.WriteLine("finished");
            }
        }

    当运行这个程序时,会出现如下异常:

    未经处理的异常: System.ServiceModel.FaultException`1[System.ServiceModel.ExceptionDetail]: 此操作将死锁,因为在当前邮件完成处理以前无法收到答复。如果要允许无序的邮件处理,则在 ServiceBehaviorAttribute 上指定可重输入的或多个 ConcurrencyMode。

    这个异常已经说得非常清楚了:当前服务不支持并发,在处理Download函数的时候信道是串行的,返回消息的时候必须先返回Download函数的处理消息,再返回DownloadComplete回调获取回调的返回结果;但Download函数本身又在等待DownloadComplete函数返回,从而形成了一个死锁。

    这个异常同时也告诉了我们一个修改方案:修改服务的ServiceBehavior的ConcurrencyMode为Reentrant或Multiple即可。

        [ServiceBehavior(ConcurrencyMode = ConcurrencyMode.Reentrant)]
        public class DownloadService

    实际上,这个问题还有另一种解决方案:如果无需等待回调完成,可以把回调函数设置为OneWay的方式;这样Download函数就不等待回调函数的返回结果了,不会因为互相等待而导致死锁。

      

      interface Inotify
        {
            [OperationContract(IsOneWay=true)]
            void DownloadComplete();
        }



    二、客户端死锁

    由于系统自己能分析出服务器端的死锁,服务器端的死锁还是比较容易发现和处理的。但客户端的死锁就不是那么容易发现了。

    经过前面的处理后,服务器端死锁问题已经解决了,客户端可以顺利处理了。这次我们把客户端代码放到WinForm版本的程序中,放在UI线程中处理。

       

     private void button1_Click(object sender, EventArgs e)
        {
            var context = new System.ServiceModel.InstanceContext(new CallBack());
            var client = new DownloadServiceClient(context);
            client.Download();
        }

    当运行上述代码时,可以发现:点击按钮后,窗口就无响应了。由于此时没有任何错误提示信息,我就直接给出错误原因了:WCF的回调函数默认是在UI线程中执行,因此就会出现Download函数等待DownloadComplete回调执行完后才返回,而DownloadComplete回调又因为Download函数又等待着Download函数返回释放UI线程才能执行,这样又形成了一个死锁。而控制台程序没有UI线程,不会出现这种死锁。

    对于这种死锁,根本的方案就是修改回调回调函数的通知线程,将其改为在非UI线程中执行。WCF中可以通过在客户端回调函数类中的CallbackBehaviorAttribute中控制这一行为

       

     [CallbackBehavior(ConcurrencyMode = ConcurrencyMode.Reentrant, UseSynchronizationContext = false)]
        class CallBack : DownloadServiceCallback

    我在这里还设置了CallbackBehaviorAttribute的ConcurrencyMode参数,这个参数在本例中是不必要的,但我还是习惯性的将其带上了,至于它在什么时候会用到,请读者朋友们自行分析。

  • 相关阅读:
    lintcode395-硬币排成线 II
    lintcode-394-硬币排成线
    lintcode-392-打劫房屋
    lintcode-391-数飞机
    lintcode-389-判断数独是否合法
    lintcode-387-最小差
    lintcode-384-最长无重复字符的子串
    lintcode-383-装最多水的容器
    lintcode-382-三角形计数
    爬虫笔记:初始爬虫(二)
  • 原文地址:https://www.cnblogs.com/chcong/p/4307910.html
Copyright © 2020-2023  润新知