Compactando um Dicionário WeakReference
Eu tenho uma aulaFoo com uma propriedadeIdentidade. Meu objetivo é que não haja duas instâncias deFoo com o mesmoIdentidade ao mesmo tempo.
Então eu criei um método de fábricaCreateFoo que usa um cache para retornar a mesma instância para o mesmoIdentidade.
static Foo CreateFoo(int id) {
Foo foo;
if (!cache.TryGetValue(id, out foo)) {
foo = new Foo(id);
foo.Initialize(...);
cache.Put(id, foo);
}
return foo;
}
O cache é implementado como um Dictionary <TKey, WeakReference>, com base em@JaredPar'sConstruindo uma Hashtable WeakReference:
class WeakDictionary<TKey, TValue> where TValue : class {
private readonly Dictionary<TKey, WeakReference> items;
public WeakDictionary() {
this.items = new Dictionary<TKey, WeakReference>();
}
public void Put(TKey key, TValue value) {
this.items[key] = new WeakReference(value);
}
public bool TryGetValue(TKey key, out TValue value) {
WeakReference weakRef;
if (!this.items.TryGetValue(key, out weakRef)) {
value = null;
return false;
} else {
value = (TValue)weakRef.Target;
return (value != null);
}
}
}
O problema é que os WeakReferences permanecem no dicionário depois que seus alvos foram coletados como lixo. Isto implica a necessidade de alguma estratégia como manualmente "coleta de lixo" mortos WeakReferences, como explicado por@Pascal Cuoq emO que acontece com um WeakReference após GC de WeakReference.Target.
Minha pergunta é:Qual é a melhor estratégia para compactar um dicionário WeakReference?
As opções que vejo são:
Não remova WeakReferences do dicionário. IMO isso é ruim, porque o cache é usado no tempo de vida completo do meu aplicativo, emuito de WeakReferences mortos irá acumular ao longo do tempo.
Ande o dicionário inteiro em cadaColocar eTryGetValuee remova WeakReferences mortos. Isso anula um pouco o propósito de um dicionário porque ambas as operações se tornamEm).
Ande o dicionário inteiro periodicamente em um segmento de plano de fundo. Qual seria um bom intervalo, dado que não sei o padrão de uso deCreateFoo?
Anexe cada KeyValuePair inserido a uma lista encadeada de final duplo. Cada chamada paraColocar eTryGetValue examina o chefe da lista. Se o WeakReference estiver ativo, mova o par para o final da lista. Se estiver morto, remova o par da lista e o WeakReference do Dicionário.
Implemente uma tabela de hash personalizada com a menor diferença de que, quando um bucket está cheio, WeakReferences mortos são removidos primeiro do bucket antes de continuar como de costume.
Existem outras estratégias?
A melhor estratégia é provavelmente um algoritmo com complexidade de tempo amortizado. Existe tal estratégia?