我的理解是,存在于 finalizer 队列中将防止任何对象或被强引用的对象被回收,但它当然不能保护它们免受最终化。在正常情况下,一旦对象完成最终化,它将从队列中删除,并且它所引用的任何对象将不再受到保护,可以在下一个 GC 通过时进行回收。在调用 finalizer 时,finalizer 可能已经针对对象引用的任何组合进行了调用;不能指望 finalizer 按任何特定顺序进行调用,但是您持有的对象引用应该仍然有效。
很明显,finalizer 绝不能获取锁,也不能尝试创建新对象。但是,假设我有一个订阅某些事件的对象和另一个实际使用这些事件的对象。如果后者的垃圾收集变得可行,我希望前者的对象在尽可能短的时间内取消订阅事件。请注意,前者的对象直到没有任何活动对象持有其订阅时才会变得不适合最终化。
是否可以拥有一个无锁链接列表堆栈或队列,其中包含需要取消订阅的对象,并且主对象的 finalizer 将对其他对象的引用放在堆栈/队列上?链接列表项对象必须在创建主对象时分配(因为在 finalizer 中分配将被禁止),并且可能需要使用诸如计时器事件之类的东西来轮询队列(因为事件取消订阅必须在 finalizer 线程之外运行,而且将一个线程作为等待 finalizer 队列中出现某些内容的唯一目的可能很愚蠢),但是如果 finalizer 可以安全地引用其预分配的链接列表对象和与其类相关联的主队列对象,则可以允许在最终化后的大约 15 秒内取消订阅事件。
这样做是否明智?(注:我正在使用 .net 2.0;此外,尝试添加到堆栈或队列可能会在 Threading.Interlocked.CompareExchange 上旋转几次,但我不希望它被卡住太久)。
编辑
当然,任何订阅事件的代码都应该实现 IDisposable,但是可处置的东西并不总是正确处置。如果是这样,就不需要 finalizers 了。
我担心的情况是以下情况之一:实现 iEnumerator(of T) 的类钩子挂接到其关联类的 changeNotify 事件上,以便在底层类更改时可以合理地处理枚举(是的,我知道 Microsoft 认为所有枚举器都应该放弃,但有时一个可以继续工作的枚举器将更有用)。这个类的实例可能在数天或数周内被枚举数千甚至数百万次,但在此期间不会进行更新。
理想情况下,枚举器不会被遗忘而没有被处理,但有时在一些不适用于“foreach”和“using”的情况下使用枚举器(例如,某些枚举器支持嵌套枚举)。一个经过精心设计的终结器可能允许处理这种情况。
顺便提一下,我要求任何需要在更新中继续的枚举都必须使用泛型IEnumerable(of T);不支持iDisposable的非泛型形式如果集合被修改,则必须抛出异常。