我有一个表单,它“监听”在其他地方引发的事件(不是在表单本身,也不是它的子控件之一)。事件由即使在处理 Form 后仍存在的对象引发,并且可能在创建 Form 句柄的线程以外的线程中引发,这意味着我需要在事件处理程序中执行 Invoke (以显示更改形式,例如)。
在 Dispose(bool)
形式的方法(覆盖)中,我取消订阅了调用此方法时可能仍被订阅的所有事件。但是,Invoke 有时仍会从其中一个事件处理程序调用。我假设这是因为事件处理程序在事件取消订阅前的片刻被调用,然后操作系统将控制权切换到执行的处置方法,然后将控制权返回给处理程序,该处理程序在已处置的对象上调用 Invoke 方法。
锁定线程没有帮助,因为对 Invoke 的调用将锁定调用线程,直到主线程处理调用的方法。这可能永远不会发生,因为主线程本身可能正在等待释放调用调用线程已占用的对象的锁,从而造成死锁。
因此,简而言之,当表单订阅了可能在不同线程中引发的外部事件时,我该如何正确处理它?</p>
这是一些关键方法目前的样子。这种方法存在我上面描述的问题,但我不确定如何纠正它们。
这是一个处理模型数据部分变化的事件处理器:
private void updateData()
{
if (model != null && model.Data != null)
{
model.Data.SomeDataChanged -= new MyEventHandler(updateSomeData);
model.Data.SomeDataChanged += new MyEventHandler(updateSomeData);
}
updateSomeData();
}
这是一个必须对 View 进行更改的事件处理程序:
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);
}
}
更新(根据 Alan 的要求):
我从不显式调用 Dispose 方法,我让框架完成它。到目前为止,死锁只在应用程序关闭时发生。在我进行锁定之前,我有时会在简单地关闭表单时抛出一些异常。
最佳答案
有两种方法可供考虑。一种是在 Form
中有一个锁定对象,并且在锁定中发生对 Dispose
和 BeginInvoke
调用的内部调用;由于 Dispose
和 BeginInvoke
都不应该花费很长时间,因此代码永远不必为锁定等待很长时间。
另一种方法是只声明由于 Control.BeginInvoke
/Form.BeginInvoke
中的设计错误,这些方法有时会抛出实际上无法避免的异常如果操作是否发生在已经被处置的表单上并不重要,则应该简单地吞下它。
关于c# - 如何正确处置 Form,而不会有从已处置对象上的另一个线程调用 Invoke 的风险?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/11351646/