我很清楚终结器通常用于控制非托管资源。在什么情况下终结器可以处理托管的?
我的理解是,终结器队列中的存在将阻止任何对象或由此强烈引用的对象被收集,但它(当然)不会保护它们免于终结。在正常的事件过程中,一旦对象完成,它将从队列中删除,并且它引用的任何对象将不再受到保护,不会在下一次 GC 传递时被收集。在调用终结器时,可能已经为对象引用的任何对象组合调用了终结器;不能依赖以任何特定顺序调用终结器,但持有的对象引用应该仍然有效。
很明显,终结器绝不能获取锁,也不能尝试创建新对象。但是,假设我有一个订阅某些事件的对象,以及另一个实际使用这些事件的对象。如果后一个对象有资格进行垃圾回收,我希望前一个对象尽快取消订阅事件。请注意,在没有任何活动对象持有它的订阅之前,前一个对象永远不会有资格完成。
是否有一个无锁链表堆栈或需要取消订阅的对象队列,并让主对象的终结器引用堆栈/队列上的另一个对象?必须在创建主对象时分配链表项对象(因为禁止在终结器中分配),并且可能需要使用诸如计时器事件之类的东西来轮询队列(因为事件取消订阅将不得不在终结器线程之外运行,并且拥有一个唯一目的是等待终结器队列中出现的线程可能是愚蠢的),但是如果终结器可以安全地引用其预分配的链表对象以及与其类关联的主队列对象,
这是个好主意吗?(注意:我使用的是 .net 2.0;此外,尝试添加到堆栈或队列可能会在 Threading.Interlocked.CompareExchange 上旋转几次,但我不希望它会被卡住很长时间)。
编辑
当然,任何订阅事件的代码都应该实现 iDisposable,但一次性的东西并不总是被正确处理。如果有,就不需要终结器了。
我关心的场景类似于以下内容:实现 iEnumerator(of T) 的类挂钩到其关联类的 changeNotify 事件,以便在底层类发生更改时可以明智地处理枚举(是的,我知道微软认为所有枚举数应该简单地放弃,但有时可以继续工作的枚举器会更有用)。在几天或几周的过程中,一个类的实例很可能被枚举了数千甚至数百万次,但在此期间根本没有更新。
理想情况下,枚举器在不被释放的情况下永远不会被遗忘,但枚举器有时用于“foreach”和“using”不适用的上下文中(例如,一些枚举器支持嵌套枚举)。精心设计的终结器可能会提供处理这种情况的方法。
顺便说一句,我要求任何应该通过更新继续的枚举必须使用通用的 IEnumerable(of T); 如果集合被修改,不处理 iDisposable 的非泛型表单将不得不抛出异常。