清理 .net WeakReferences 的缓存

Cleaning up a cache of .net WeakReferences

在我的应用程序中,我有一个 Dictionary<int, WeakReference<Foo>> 来缓存从文件中读取的 Foos,其中键是文件中的索引。由于 Foo 是可变的,只要在该索引处对 Foo 的任何引用都必须保持活动状态(以便任何持有者以及从 Foo来源)。

一旦 Foo 完全未被引用,我想删除 Dictionary 条目。我最初的想法是将 Foo 的终结器从缓存中删除,但这会导致在缓存插入时触发 GC 时内部状态不一致。我试图牢记终结器不能用于托管内存的概念。那么有没有办法做到这一点?

在我看来,您正在寻找的是一个 ConditionalWeakTable<TKey, TValue>,您将 Foo 存储为您的键,而不是您的值:

The ConditionalWeakTable class differs from other collection objects in its management of the object lifetime of keys stored in the collection. Ordinarily, when an object is stored in a collection, its lifetime lasts until it is removed (and there are no additional references to the object) or until the collection object itself is destroyed. However, in the ConditionalWeakTable class, adding a key/value pair to the table does not ensure that the key will persist, even if it can be reached directly from a value stored in the table (for example, if the table contains one key, A, with a value V1, and a second key, B, with a value P2 that contains a reference to A). Instead, ConditionalWeakTable automatically removes the key/value entry as soon as no other references to a key exist outside the table.

我最终用我正在调用的项目解决了这个问题 BackgroundFinalizer (source on github)。本质上,这允许您定义一个 "finalizer",它在垃圾收集期间不是 运行,而是在后台 运行。所以在我这里的特殊情况下,当 Foo 被垃圾收集时,一个从缓存中删除条目的函数被调度到独立于 GC 的工作线程中的 运行 ,所以我们避免了任何阻塞情况处于终结器或不一致的内部状态。

由于无法在对象被取消引用时自动删除 Dictionary 条目,另一个想法是在了解它后立即通过编写 cache-getting 函数自行删除它像那样:

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