【问题标题】:Disposing Microsoft.Owin.Hosting.WebApp throws 'System.ObjectDisposedException'处理 Microsoft.Owin.Hosting.WebApp 会引发“System.ObjectDisposedException”
【发布时间】:2018-02-01 23:42:55
【问题描述】:

我们的 WPF 应用程序中有一个自托管的 SignalR 服务器。 WebApp 在应用程序启动时启动。在应用程序退出时,我们处理 WebApp。

    public void Start()
    {
        myWebApp = WebApp.Start<MyApp>(url);
    }

    private void Dispose(bool isDisposing)
    {
        if (disposed) return;

        if (isDisposing)
            myWebApp.Dispose();

        disposed = true;
    }

对 myWebApp.Dispose() 的调用会引发“System.ObjectDisposedException”。 难道我做错了什么? Microsoft.Owin.* dll 的版本为 2.1.0,SignalR 自托管为 2.0.3

更新:原来这是我在 Visual Studio 中看到的第一个机会异常,因为设置“打破 clr 异常”处于活动状态。这个异常似乎是在内部处理的,不会冒泡到我们的代码中

【问题讨论】:

  • 这可能是您的第一次机会,但如果使用 NLog 中间件:appBuilder.UseNLog() 它将异常记录为错误。

标签: .net wpf signalr owin


【解决方案1】:

在浏览了 Katana 源代码后,我找到了这个问题的原因。它是Microsoft.Owin.Host.HttpListener.OwinHttpListener.ProcessRequestsAsync() 方法。它启动包含 _listener.GetContextAsync() 在 try-catch 部分中调用私有 HttpListener 实例的 while 循环。

类还实现IDisposable 并包含Dispose() 方法。此方法处理私有 HttpListener 实例。

当您调用WebApp.Start() 时,它会返回IDisposable 的实例,该实例只有Dispose() 方法,会释放OwinHttpListener

所以,当你处置它时,你调用它的OwinHttpListenerDispose() 方法,它处置私有的HttpListener

但同时ProcessRequestsAsync()调用_listener.GetContextAsync(),但_listener已经被释放并抛出ObjectDisposedExceptioncatch 阻止记录异常并从ProcessRequestsAsync() 返回。

我认为,ProcessRequestsAsync() 中的双重检查锁定可能是一个不错的选择。

private async void ProcessRequestsAsync()
{
    while (_listener.IsListening && CanAcceptMoreRequests)
    {
        Interlocked.Increment(ref _currentOutstandingAccepts);
        HttpListenerContext context;
        try
        {
            context = await _listener.GetContextAsync();
        }
        (SOME_OTHER_CATCHES)
        catch (ObjectDisposedException ode)
        {
            // These happen if HttpListener has been disposed
            Interlocked.Decrement(ref _currentOutstandingAccepts);
            LogHelper.LogException(_logger, "Accept", ode);
            return;
        }
        (SOME_OTHER_CODE)
    }
}

public void Dispose()
{
    if (_listener.IsListening)
    {
        _listener.Stop();
    }

    ((IDisposable)_listener).Dispose();
}

【讨论】:

  • 所以如果不重写 Katana 就无法解决问题。对吗?
  • @SerG 是的,是的。您所能做的就是离开由 WebApp.Start() 返回的对象,未处理。
  • 我不确定双重检查锁定在哪里会有所帮助。但是,似乎 Dispose 方法需要做更多的工作来关闭仍在尝试执行操作的异步进程,而侦听器正在其下方被处理掉。
猜你喜欢
  • 1970-01-01
  • 2014-12-03
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2020-10-16
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多