在 Form.Dispose() 方法中安全调用
Safe to Invoke within Form.Dispose() method
我有一个 ApplicationContext
和一个 Form
。各种异步通信和定时线程可能偶尔需要重新启动应用程序,但是在我重新启动之前我必须手动处理 MyApplicationContext
这需要立即 [=39] 释放需要的资源=] 新进程启动时。
在这种情况下,似乎仅调用 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
class 的代码时,您应该已经确保代码在拥有该对象的 UI 线程中执行。
我有一个 ApplicationContext
和一个 Form
。各种异步通信和定时线程可能偶尔需要重新启动应用程序,但是在我重新启动之前我必须手动处理 MyApplicationContext
这需要立即 [=39] 释放需要的资源=] 新进程启动时。
在这种情况下,似乎仅调用 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
class 的代码时,您应该已经确保代码在拥有该对象的 UI 线程中执行。