处理 Microsoft.Owin.Hosting.WebApp 时抛出 'System.ObjectDisposedException' 异常

8
我们在 WPF 应用程序中拥有一个自托管的 SignalR 服务器。Web 应用程序会在应用程序启动时启动,并在应用程序退出时被销毁。
    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.* dlls的版本为2.1.0,SignalR self host的版本为2.0.3。 更新:事实证明这是第一次机会异常,我可以在Visual Studio中看到它,因为设置“在clr异常上中断”是活动的。这个异常似乎被内部处理,不会冒泡到我们的代码中。

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

3
在探索Katana源代码后,我发现了这个问题的原因。它是由Microsoft.Owin.Host.HttpListener.OwinHttpListener.ProcessRequestsAsync()方法引起的。它在try-catch块中启动一个包含私有HttpListener实例的_listener.GetContextAsync()调用的while循环。
此外,该类实现了IDisposable并包含一个Dispose()方法。此方法释放私有的HttpListener实例。
当您调用WebApp.Start()时,它返回一个IDisposable实例,只有一个Dispose()方法,该方法释放OwinHttpListener
因此,当您处理它时,您会调用其OwinHttpListenerDispose()方法,从而释放私有的HttpListener
但是同时ProcessRequestsAsync()调用_listener.GetContextAsync(),但是_listener已经被释放并且抛出了ObjectDisposedException异常。catch块记录异常并从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
1
@SerG 是的,没错。你所能做的就是离开由WebApp.Start()返回的对象未被处理。 - Tim
我不确定双重检查锁定在这里会有什么帮助。然而,Dispose方法似乎需要做更多的工作来关闭异步进程,因为在其下面被处理时,该进程仍在尝试执行操作。 - Matthew Walton

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接