在 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 线程中执行。