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

Posted

技术标签:

【中文标题】在 Form.Dispose() 方法中安全调用【英文标题】:Safe to Invoke within Form.Dispose() method 【发布时间】:2016-06-14 15:41:00 【问题描述】:

我有一个ApplicationContext 和一个Form。各种异步通信和定时线程有时可能需要重新启动应用程序,但是在我重新启动之前,我必须手动处理 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 被覆盖的 dispose 处理程序是否安全?

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);
    

【问题讨论】:

【参考方案1】:

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

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

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

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

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

【讨论】:

确实,我是在 侥幸逃脱。我认为将我的requestRestart 方法移动到Form 类中会很容易,这样如果需要跨线程调用,它就会在调用堆栈的底部完成。但是这种情况下,没有办法保证发起重启请求的线程一定是FormUI线程——比如网络IO到达的时候。 你可以在任何你喜欢的地方发起重启请求,只要你确保在真正处理重启请求之前的清理之前你已经移动到UI线程。即使在那里,您可能会发现使用SynchronizationContext 而不是Form 实例很有用/可取,因为大概Form 实例将作为清理的一部分被处置(即您仍然有调用@ 的问题987654338@ 将在 Invoke() 方法返回之前被释放的对象上)。 我接受了您的好建议,并使用主表单中的SynchronizationContext。谢谢彼得。我对现在的情况感觉好多了

以上是关于在 Form.Dispose() 方法中安全调用的主要内容,如果未能解决你的问题,请参考以下文章

Activex界面已显示,调用方法报undefined的处理办法

d的破坏与安全

Java+线程内部调用实例方法会多线程安全吗?

安全评价方法中的L、S、R、D是啥意思

java多线程并发去调用一个类的静态方法安全性探讨

使用spring-security更改方法调用的安全性上下文