在 Form.Dispose() 方法中调用是安全的
本文关键字:调用 安全 方法 Form Dispose | 更新日期: 2023-09-27 18:36:56
我有一个Form
的ApplicationContext
。各种异步通信和定时线程有时可能需要重新启动应用程序,但是在重新启动之前,我必须手动释放MyApplicationContext
这需要释放新进程启动时立即需要的资源。
在这种情况下,仅调用Application.Restart()
似乎不能足够快地释放资源。
在对MyApplicationContext.Dispose()
的调用中,对base.Dispose(disposing)
最终调用Form.Dispose()
方法的后续调用,并且由于这可能源自各种线程,因此我看到了跨线程操作异常的发生。
/// MyApplicationContext.requestRestart()
private void requestRestart()
{
this.Dispose(); // dispose of applicationcontext
Application.Restart();
}
导致...
/// MyApplicationContext.Dispose(bool)
protected override void Dispose(bool disposing)
{
/// dispose stuff
base.Dispose(disposing);
}
导致...
/// MainForm.Dispose(bool)
protected override void Dispose(bool disposing)
{
/// dispose stuff
base.Dispose(disposing);
}
可以从任何线程调用。
像这样在 Form
UI 线程上Invoke
重写的释放处理程序是否安全?
protected override void Dispose(bool disposing)
{
if (this.InvokeRequired)
{
this.Invoke(new Action(() => Dispose(disposing)));
}
else
{
if (disposing && (components != null))
{
components.Dispose();
}
base.Dispose(disposing);
}
}
不,从Dispose()
实现调用Invoke()
是不安全的。
好吧,既然我已经说过了:当然,你可能会侥幸逃脱。但这是一个非常糟糕的主意,它真的可能导致真正的问题。
Dispose()
方法应该很简单。它不应访问托管对象,并且应快速完成。调用Invoke()
可能会导致不确定的延迟,具体取决于程序中的其他情况,甚至完全死锁。
除此之外,Dispose()
方法中的一个重要规则是不访问托管对象。至少,您的实现需要首先检查disposing
,以确保在从终结器调用时不会尝试调用Invoke()
。此外,释放当前用于调用 Invoke()
方法的对象也绝对不是一个好主意。如果您确实必须使用跨线程调用,正确的方法是检索当前对象的SynchronizationContext
并使用它而不是调用 Control.Invoke()
。
但实际上,此处要做的正确的做法是修复重新启动逻辑,以便整个释放操作发生在拥有正在释放的对象的 UI 线程中。您的对象不是应该负责将执行移动到正确线程的对象;应改为使用这些对象的代码。在涉及 Form
类的代码时,您应该已经确保代码在拥有该对象的 UI 线程中执行。