在 Form.Dispose() 方法中调用是安全的

本文关键字:调用 安全 方法 Form Dispose | 更新日期: 2023-09-27 18:36:56

我有一个FormApplicationContext。各种异步通信和定时线程有时可能需要重新启动应用程序,但是在重新启动之前,我必须手动释放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);
    }
}

在 Form.Dispose() 方法中调用是安全的

不,从Dispose()实现调用Invoke()是不安全的。

好吧,既然我已经说过了:当然,你可能会侥幸逃脱。但这是一个非常糟糕的主意,它真的可能导致真正的问题。

Dispose()方法应该很简单。它不应访问托管对象,并且应快速完成。调用Invoke()可能会导致不确定的延迟,具体取决于程序中的其他情况,甚至完全死锁。

除此之外,Dispose()方法中的一个重要规则是不访问托管对象。至少,您的实现需要首先检查disposing,以确保在从终结器调用时不会尝试调用Invoke()。此外,释放当前用于调用 Invoke() 方法的对象也绝对不是一个好主意。如果您确实必须使用跨线程调用,正确的方法是检索当前对象的SynchronizationContext并使用它而不是调用 Control.Invoke()


但实际上,此处要做的正确的做法是修复重新启动逻辑,以便整个释放操作发生在拥有正在释放的对象的 UI 线程中。您的对象不是应该负责将执行移动到正确线程的对象;应改为使用这些对象的代码。在涉及 Form 类的代码时,您应该已经确保代码在拥有该对象的 UI 线程中执行。