UI线程侦听由其他线程

时间:2016-12-06 18:01:06

标签: c# multithreading winforms

有一天,有一个控制台服务器应用程序需要支持GUI层。我创建了一个包含gui表单的类库,从控制台应用程序项目中引用它,然后让表单实例化并在单独的线程上运行。所以服务器对象和表单对象存在于不同的线程中。

服务器是游戏服务器。它维护当前放置在地图上的游戏对象的集合。当然,随着时间的推移,它们的数量会增加或减少。该表格必须实时显示所有这些对象。所以当然我添加了一个事件,服务器会在窗体的对象列表发生更改时调用。

class Server {
    event Action<IEnumerable> ObjectListChanged:
}

当表单附加到服务器时,它将添加处理程序。

Server server = new Server ();
var form = new VisializerForm ();
server.ObjectListChanged += form.OnObjectListChanged;

当表单关闭时,它将从服务器中删除处理程序。

form.Closing += (s,e) => {
    server -= form.OnObjectListChanged:
};

当事件调用发生在服务器线程中时,OnObjectListChanged的实现将包含对InvokeRequired属性的检查。

class VisualizerForm : Form {
    void OnObjectListChanged(IEnumerable objects){
        if( !Visible || IsDisposed || !IsHandleCreated ) return;

        if( InvokeRequired ) {
            Invoke ((Action)(()=>OnObjectListChanged (objects)));
            return;
        }

        // ....
    }
}

这一点很有效,直到我们关闭表单为止。在窗体关闭后调用事件时抛出异常,实际上不应该在Form.Closing事件中删除事件处理程序时发生,即“无法访问已处置对象&#39; Visualizer&#” 39;

有趣的是,当我通过调试器检查时,事件显然没有任何处理程序(在visual studio的监视窗口中列出时显示为null)但是线程到达事件处理程序内部的Form.Invoke调用行有什么关系。很可能它暗示我在某些时候搞砸了多线程。我究竟做错了什么?这可能会发生什么?

很抱歉格式错误我将其发布到手机上,而不是习惯了。

更改了下面的代码并仍然获得相同的异常。

    public void OnObjectListChanged(IEnumerable objects)
    {
        if (IsDisposed || Disposing || !Visible || !IsHandleCreated) return;

        if (InvokeRequired) {
            try {
                Invoke((Action)(() => OnFieldObjectListChanged(zoneId, channel, removed, added)));
            } catch (Exception e) {
                Debugger.Break(); // <-- server thread enters here.
            }
            return;
        }

        // ....
    }

以下是我在catch子句中得到的异常和堆栈跟踪:

   위치: System.Windows.Forms.Control.MarshaledInvoke(Control caller, Delegate method, Object[] args, Boolean synchronous)
   위치: System.Windows.Forms.Control.Invoke(Delegate method, Object[] args)
   위치: System.Windows.Forms.Control.Invoke(Delegate method)
   위치: VisualZoneServerLib.Visualization.VisualZoneServerWindow.OnFieldObjectListChanged(Int32 zoneId, Int32 channel, IList`1 removed, IList`1 added) 파일 D:\Server\ZoneServerLib.Visualizer\Visualization\VisualZoneServerWindow.cs:줄 310

与此同时,UI线程在Dispose方法的覆盖中停止。

    protected override void Dispose(bool disposing)
    {
        if (disposing) {
            components?.Dispose();
            m_timeLineWindow?.Dispose();
            m_iesSnapshots?.Dispose();
        }
        base.Dispose(disposing); // <- UI thread stopped here
    }

再次搜索关键字&#39; winform&#39; &#39;调用&#39; &#39;处置&#39;并找到了this thread。这似乎是在讨论我遇到的同样问题。

1 个答案:

答案 0 :(得分:2)

我过去遇到过类似的问题,我遇到的问题是Invoke ((Action)(()=>OnObjectListChanged (objects)));必须在消息泵队列中等待处理请求。如果表单已关闭并处理完毕,但在处理该项目后,您仍然在队列中有待处理的请求,它将访问您处理的表单。

一个简单的修复方法是更改​​您检查的内容的顺序,以便首先检查IsDisposed。同时也在那里抛出Disposing支票。

void OnObjectListChanged(IEnumerable objects){
    if( IsDisposed || Disposing || !Visible || !IsHandleCreated ) return;

    if( InvokeRequired ) {
        Invoke ((Action)(()=>OnObjectListChanged (objects)));
        return;
    }

    // ....
}