我有一个表单,它“监听”在其他地方引发的事件(不是在表单本身,也不是它的一个子控件)。事件由即使在 Form 被释放后仍然存在的对象引发,并且可能在创建 Form 句柄的线程之外的线程中引发,这意味着我需要在事件处理程序中执行 Invoke(以显示更改形式,例如)。
在Dispose(bool)
表单(重写)的方法中,我取消了在调用此方法时可能仍订阅的所有事件。但是,有时仍会从其中一个事件处理程序调用 Invoke。我认为这是因为事件处理程序在事件取消订阅前一刻被调用,然后操作系统将控制权切换到执行的 dispose 方法,然后将控制权返回给调用已处理对象上的 Invoke 方法的处理程序。
锁定线程无济于事,因为对 Invoke 的调用将锁定调用线程,直到主线程处理调用的方法。这可能永远不会发生,因为主线程本身可能正在等待调用调用线程已获取的对象上的锁释放,从而产生死锁。
所以,简而言之,当订阅可能在不同线程中引发的外部事件时,如何正确处理表单?
以下是目前一些关键方法的外观。这种方法遇到了我上面描述的问题,但我不确定如何纠正它们。
这是一个处理模型数据部分更改的事件处理程序:
private void updateData()
{
if (model != null && model.Data != null)
{
model.Data.SomeDataChanged -= new MyEventHandler(updateSomeData);
model.Data.SomeDataChanged += new MyEventHandler(updateSomeData);
}
updateSomeData();
}
这是一个必须对视图进行更改的事件处理程序:
private void updateSomeData()
{
if (this.InvokeRequired) this.myInvoke(new MethodInvoker(updateSomeData));
else
{
// do the necessary changes
}
}
和 myInvoke 方法:
private object myInvoke(Delegate method)
{
object res = null;
lock (lockObject)
{
if (!this.IsDisposed) res = this.Invoke(method);
}
return res;
}
我对方法的覆盖Dispose(bool)
:
protected override void Dispose(bool disposing)
{
lock (lockObject)
{
if (disposing)
{
if (model != null)
{
if (model.Data != null)
{
model.Data.SomeDataChanged -= new MyEventHandler(updateSomeData);
}
// unsubscribe other events, omitted for brevity
}
if (components != null)
{
components.Dispose();
}
}
base.Dispose(disposing);
}
}
更新(根据艾伦的要求):
我从不显式调用 Dispose 方法,我让框架来完成。到目前为止,死锁仅在应用程序关闭时发生。在我进行锁定之前,我有时会在表单关闭时抛出一些异常。