在我的应用程序中,我有一个Dictionary<int, WeakReference<Foo>>
来缓存从文件中读取的Foo
,其中键是文件中的索引。由于Foo
是可变的,只要对该索引处的Foo
有任何引用,缓存条目就必须保持活动状态(这样,任何持有者以及从Foo
源读取新内容的任何人都可以看到更改)。
一旦完全取消引用Foo
,我想删除Dictionary
条目。我最初的想法是让Foo
的终结器将自己从缓存中移除,但这导致在插入缓存时触发GC时内部状态不一致。我试图牢记终结器不能用于托管内存的概念。那么有什么办法可以做到这一点吗?
在我看来,您想要的是ConditionalWeakTable<TKey, TValue>
,在这里您存储Foo
作为密钥,而不是您的值:
ConditionalWeakTable类不同于其他集合对象在其密钥的对象生存期管理中存储在集合中。通常,当对象存储在集合,其生存期将持续到删除为止(并且没有对对象的附加引用)或直到集合对象它本身就被摧毁了。但是,在ConditionalWeakTable类中,向表中添加键/值对并不能确保即使可以直接从存储在表中的值(例如如果表包含一个键,A、 值为V1的第二个键B,值为P2,包含参考a)。相反,ConditionalWeakTable自动删除键/值条目表外存在对键的引用
我最终用一个名为BackgroundFinalizer的项目(github上的源代码)解决了这个问题。本质上,这允许您定义一个"终结器",它在垃圾收集期间不运行,而是在后台运行。因此,在我的特定情况下,当Foo
被垃圾收集时,从缓存中删除条目的函数被安排在独立于GC的工作线程中运行,因此我们避免了在终结器中阻塞或内部状态不一致的任何情况。
由于当对象被取消引用时,没有办法自动删除Dictionary
条目,另一个想法是在了解它后立即自己删除它,方法是编写这样的缓存获取函数:
private readonly Dictionary<int,WeakReference<Foo>> _dict;
public bool TryGetItem(int key, out Foo item) {
if (_dict.TryGetvalue(key, out var wr)) {
if (wr.TryGetTarget(out item))
return true;
else
// The weak reference is dead. We remove the dictionary entry.
_dict.Remove(key);
}
return false;
}