有一天,有一个控制台服务器应用程序需要支持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。这似乎是在讨论我遇到的同样问题。
答案 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;
}
// ....
}