4

这几乎只是为了确保我做对了:

我们有一个实现 IDisposal 模式的大型资源类。它应该(通过设计)以某种方式实现,使其能够被多次调用(即使我们尝试准确地调用它一次)。我们还实现了一个终结器,它也调用 Dispose() 方法 - 就像备份一样。如果手动调用,Dispose() 也会调用 GC.SuppressFinalize(this)。

周围有几个处置模式的例子。他们中的大多数人在处理代码的末尾调用 GC.SuppressFinalize(this) 。有人声称,最好在 Dispose() 方法的开头调用它,然后再进行任何清理。后者争辩说,这将确保 GC 不会同时调用终结器,而我们仍在清理。

问题:
似乎将 GC.SuppressFinalize 放在开头并没有更好的效果?我们还有比赛条件,对吧?那么,我们宁愿以线程安全的方式实现 Dispose() 是真的吗?

4

3 回答 3

4

GC 只清理不“可达”的对象。

正在执行代码的类仍然是“可访问的”,因为它的this指针在堆栈上。因此,在 dispose 执行时,不会调用终结器。

因此,无论您SuppressFinalize是在开始还是结束时调用都没有关系。

正如下面的评论者所指出的,CLR 实现似乎不能保证您的对象在实例方法执行时不会被垃圾收集/终结。使对象保持活动状态的唯一可能的“可靠”引用是用于调用对象上的方法的引用,但我对 JIT 内部的了解不够多,无法对此发表声明,而且它的行为可能会改变。

我将在此处留下答案,以便访问下面的讨论。

于 2011-01-17T08:47:59.883 回答
3

虽然有时可能会在存在看似活动的引用时完成对象,但这只会发生在没有其他东西会引用该对象的情况下。GC.SuppressFinalize(this) 主动引用当前对象“this”,从而保证在 GC.SuppressFinalize 执行之前它不会被最终确定。此外,对象引用存在以处置对象并且可用于 Dispose 方法的事实保证了终结器在 Dispose 开始运行之前不会排队,除非对象已死并且某个终结器(它自己的终结器) ,或其他物体的)复活它。

因为在某些情况下,对象可以在不知道的情况下被安排完成并复活,所以保护 dispose 和 finalize 免受冗余操作可能不是一个坏主意。然而,微软的模式并不是一个好的模式。可终结对象不应包含对终结不需要的任何对象的引用。如果一个对象包含托管资源和非托管资源的混合,则非托管资源应该被移动到它们自己的类中(有效地将它们变成托管资源),那么主对象将只包含托管资源。

于 2011-01-17T16:30:19.237 回答
1

Dispose 不应抛出任何异常。

我会确保 Dispose 中的所有代码都是线程安全的,这样如果它被调用,它就不会做任何奇怪的事情。通常添加检查变量是否已经为空应该可以解决问题。

在微软的例子中,我只在 Dispose 函数的末尾看到了 GC.SuppressFinalize。

于 2011-01-17T08:49:10.737 回答