Warning: file_get_contents(/data/phpspider/zhask/data//catemap/2/.net/23.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
C# 关闭正在使用回调的连接时收到WCF异常_C#_.net_Wcf_Deadlock - Fatal编程技术网

C# 关闭正在使用回调的连接时收到WCF异常

C# 关闭正在使用回调的连接时收到WCF异常,c#,.net,wcf,deadlock,C#,.net,Wcf,Deadlock,我正在使用netNamedPipeBinding执行从windows应用程序到windows服务的进程间WCF通信 现在,我的应用程序在所有其他帐户中都运行良好(正如任何使用过WCF的人都会知道的那样,在消除了我的WCF例外情况后…),但这个错误被证明是相当有弹性的 描绘我的场景:我的windows服务可以通过在windows应用程序中按下一个按钮在任何给定时间排队做一些工作,然后它通过netNamedPipeBinding进行对话,如果您不熟悉,这是一个支持回调(双向通信)的绑定,并启动执行此

我正在使用
netNamedPipeBinding
执行从windows应用程序到windows服务的进程间WCF通信

现在,我的应用程序在所有其他帐户中都运行良好(正如任何使用过WCF的人都会知道的那样,在消除了我的WCF例外情况后…),但这个错误被证明是相当有弹性的

描绘我的场景:我的windows服务可以通过在windows应用程序中按下一个按钮在任何给定时间排队做一些工作,然后它通过
netNamedPipeBinding
进行对话,如果您不熟悉,这是一个支持回调(双向通信)的绑定,并启动执行此工作的请求,(在本例中是文件上载过程)它还会每隔几秒钟将回调(事件)抛出到windows应用程序,范围从文件进度到传输速度等,因此存在一些相当紧密的客户端-服务器集成;这就是我如何将windows服务中运行的内容的进度返回到windows应用程序的方式

现在,一切都很好,WCF诸神现在对我比较满意,除了每次我过早关闭应用程序时都会收到一个令人讨厌的异常情况(这是一个完全有效的情况)。在传输过程中,回调非常频繁,我收到以下错误:

System.ServiceModel.ProtocolException:
  The channel received an unexpected input message with Action 
  'http://tempuri.org/ITransferServiceContract/TransferSpeedChangedCallback' 
  while closing. You should only close your channel when you are not expecting 
  any more input messages.
现在我理解了这个错误,但不幸的是,我不能保证在从未收到任何输入消息后关闭我的频道,因为用户可能随时关闭应用程序,因此工作仍将在windows服务的后台继续(有点像病毒扫描程序的操作方式)。用户应能够在不受干扰的情况下,随心所欲地启动和关闭win management tool应用程序

现在,在执行
Unsubscribe()之后,我立即收到错误消息
call,这是终止应用程序之前的第二个最后一个呼叫,我认为是断开WCF客户端的首选方式。在关闭连接之前,取消订阅只需从存储在win service WCF服务本地的阵列中删除客户端id即可(由于这是win服务和windows应用程序共享的实例,因为win服务可以在计划的事件中自行执行工作)并且在我执行客户端id阵列删除后,我希望(感觉)应该是干净的断开连接

结果,除了收到一个异常,我的应用程序挂起,用户界面被完全锁定,进度条和所有东西都处于中间状态,所有迹象都表明存在竞争条件或WCF死锁[叹气],但我现在对线程非常了解,我认为这是一个相对孤立的情况,从目前的情况来看,我不认为这是一个“线程”问题本身,因为它更多地说明了一个早期断开的问题,这会使我的所有线程陷入混乱,可能导致锁定

我在客户端上的
Unsubscribe()
方法如下所示:

    public void Unsubscribe()
    {
        try
        {
            // Close existing connections
            if (channel != null &&
                channel.State == CommunicationState.Opened)
            {
                proxy.Unsubscribe();
            }
        }
        catch (Exception)
        {
            // This is where we receive the 'System.ServiceModel.ProtocolException'.
        }
        finally
        {
            Dispose();
        }
    }
以及我的
Dispose()
方法,该方法应执行干净的断开连接:

    public void Dispose()
    {
        // Dispose object
        if (channel != null)
        {
            try
            {
                // Close existing connections
                Close();
                // Attempt dispose object
                ((IDisposable)channel).Dispose();
            }
            catch (CommunicationException)
            {
                channel.Abort();
            }
            catch (TimeoutException)
            {
                channel.Abort();
            }
            catch (Exception)
            {
                channel.Abort();
                throw;
            }
        }
    }
以及windows服务服务器上的WCF服务
Subscription()
对应项和类属性(供参考)(这里没有什么棘手的问题,我的异常发生在客户端):

接口:

[ServiceContract(
    CallbackContract = typeof(ITransferServiceCallbackContract), 
    SessionMode = SessionMode.Required)]
public interface ITransferServiceContract
{
    [OperationContract(IsInitiating = true)]
    bool Subscribe();

    [OperationContract(IsOneWay = true)]
    void Unsubscribe();
    ...
}
回调契约的接口,它没有做任何令人兴奋的事情,只是通过委托等调用事件。我之所以包含它是为了向您展示我的属性。我已经通过包含
UseSynchronizationContext=false
缓解了一组死锁:

[CallbackBehavior(UseSynchronizationContext = false, 
ConcurrencyMode = ConcurrencyMode.Multiple)]
public class TransferServiceCallback : ITransferServiceCallbackContract
{ ... }

真希望有人能帮助我!非常感谢=:)

哦,天哪,我发现了问题

该异常与underyling应用程序挂起无关,这只是一个预防性异常,您可以安全地捕获它

你不会相信的,我花了大约6个小时来处理这个bug,结果是
频道.Close()
锁定等待等待等待等待完成的WCF请求(在传输完成之前永远不会完成!这违背了目的!)

我只是一行接一行地强行中断,我的问题是如果我速度太慢……它永远不会挂起,因为不知何故,通道可以关闭(甚至在传输完成之前)因此,我必须中断F5,然后快速移动以捕获挂起,这就是它结束的那一行。我现在只需将超时值应用于
Close()
操作,并使用
TimeoutException
捕获它,然后如果频道无法及时关闭,则硬中止该频道

请参阅修复代码:

private void Close()
{
    if (channel != null &&
        channel.State == CommunicationState.Opened)
    {
        // If cannot cleanly close down the app in 3 seconds,
        // channel is locked due to channel heavily in use
        // through callbacks or the like.
        // Throw TimeoutException
        channel.Close(new TimeSpan(0, 0, 0, 3));
    }
}

public void Dispose()
{
    // Dispose object
    if (channel != null)
    {
        try
        {
            // Close existing connections
            // *****************************
            // This is the close operation where we perform 
            //the channel close and timeout check and catch the exception.
            Close();

            // Attempt dispose object
            ((IDisposable)channel).Dispose();
        }
        catch (CommunicationException)
        {
            channel.Abort();
        }
        catch (TimeoutException)
        {
            channel.Abort();
        }
        catch (Exception)
        {
            channel.Abort();
            throw;
        }
    }
}
我很高兴这个错误终于解决了!我的应用程序现在在3秒超时后完全关闭,不管当前WCF服务状态如何,我希望我能帮助其他人,他们发现自己也遇到了类似的问题


Graham

我不知道具体的问题,但对于信息,线程混乱听起来可能是由于WCF如何使用同步上下文(这是winforms中的表单等)造成的.谢谢Marc,是的,这抓住了我,我通过阅读这个问题缓解了一系列僵局,诀窍是在回调合同上设置
UseSynchronizationContext=false
))我将把这个添加到我的示例中。啊,对;很高兴看到你已经把它盖好了;派普。我真的很讨厌处理wcf异常,对于这个项目我完全不喜欢!正如你所看到的,根据我不得不写的问题的大小,我必须讲述我的生活故事来解释:)这个例外与underyling应用程序挂起无关,这只是一个预防性的例外,你可以安全地抓住它。你有没有一个参考链接可以说明吞咽这样的原始异常的安全性?我有完全相同的问题。
private void Close()
{
    if (channel != null &&
        channel.State == CommunicationState.Opened)
    {
        // If cannot cleanly close down the app in 3 seconds,
        // channel is locked due to channel heavily in use
        // through callbacks or the like.
        // Throw TimeoutException
        channel.Close(new TimeSpan(0, 0, 0, 3));
    }
}

public void Dispose()
{
    // Dispose object
    if (channel != null)
    {
        try
        {
            // Close existing connections
            // *****************************
            // This is the close operation where we perform 
            //the channel close and timeout check and catch the exception.
            Close();

            // Attempt dispose object
            ((IDisposable)channel).Dispose();
        }
        catch (CommunicationException)
        {
            channel.Abort();
        }
        catch (TimeoutException)
        {
            channel.Abort();
        }
        catch (Exception)
        {
            channel.Abort();
            throw;
        }
    }
}