.net - ¿Cómo administrar objetos IDisposable que se almacenan en caché?
caching (6)
Tengo un objeto que es costoso de crear, que utiliza algunos recursos no administrados que deben liberarse explícitamente cuando se hace con y para implementar IDisposable (). Me gustaría un caché, por ejemplo, de estos recursos caros para que el costo de creación se reduzca al mínimo, pero tengo problemas para saber cómo hacer frente a la eliminación.
Si los métodos que usan los objetos son responsables de la eliminación, entonces termino con instancias dispuestas en el caché, que luego tienen que ser recreadas, derrotando el punto del caché. Si no dispongo los objetos en los métodos que los utilizan, nunca serán eliminados. Pensé que podría deshacerme de ellos cuando salgan de la memoria caché, pero luego podría terminar desechando una instancia que todavía está siendo utilizada por un método.
¿Es válido simplemente dejarlos fuera del alcance y ser recogidos por el recolector de basura y liberar los recursos en ese punto? Esto se siente mal y en contra de la idea de que sean desechables ...
Para (des) citar a Raymond Chen: cada caché sin una política de caducidad es una fuga
Por lo tanto, establezca una política de caducidad de caché clara y deje que el caché los descarte como el caso normal. Esto todavía deja el cierre de la aplicación para ser manejado.
Si sus recursos no administrados son propiedad del proceso, puede dejar que el proceso los libere al cierre.
Si los recursos no administrados no son propiedad del proceso, debe detectar el cierre y explícitamente Desechar los elementos almacenados en caché.
Si no puede detectar el cierre del proceso de manera confiable, y los recursos gestionados son costosos, los no administrados no lo son, separe los recursos administrados de los no administrados, y deje que el caché guarde únicamente los gestionados.
Cuando los recursos no administrados son costosos, por lo que necesitan el almacenamiento en caché, y no son propiedad del proceso, y no puede detectar el cierre del proceso de manera confiable y no puede permitirse filtrarlos, entonces su problema no puede ser resuelto.
Puede desacoplar los recursos no administrados de la instancia administrada y usar un administrador de caché para mantener un conjunto de recursos no administrados. El objeto gestionado intentará adquirir una instancia del recurso no gestionado del administrador de caché que creará uno o dará una instancia gratuita desde el caché y lo devolverá al administrador de caché (en lugar de eliminarlo él mismo) en el momento de su eliminación. . El administrador de caché será el único responsable de asignar y liberar recursos no administrados cuando lo considere necesario.
Puede resolver esto con una fábrica de clase e IDisposable. Por ejemplo:
public class CachedObject : IDisposable {
private int mRefCount;
private CachedObject(int something) {
mRefCount = 1;
}
public static CachedObject CreateObject(int something) {
CachedObject obj = LookupInCache(something);
if (obj != null) Interlocked.Increment(ref obj.mRefCount);
else obj = new CachedObject(something);
return obj;
}
private static CachedObject LookupInCache(int something) {
CachedObject obj = null;
// Implement cache lookup here, don''t forget to lock
//..
return obj;
}
public void Dispose() {
int cnt = Interlocked.Decrement(ref mRefCount);
if (cnt == 0) {
// Remove from cache
//
}
}
}
Un objeto debe ser eliminado por la clase que lo crea. Dado que las personas que llaman no han creado los elementos en la memoria caché, tampoco tienen ningún tipo de disposición comercial sobre ellos.
Me gustaría asegurarme de que su método de fábrica se llame algo así como "Obtener clase " en lugar de "Crear clase " para enfatizar que la persona que llama no es responsable de la creación y, por lo tanto, no de su eliminación.
Los objetos desechables siempre deben tener un propietario claro que sea responsable de eliminarlos. Sin embargo, este no es siempre el objeto que los creó. Además, la propiedad puede ser transferida.
Al darse cuenta de esto, la solución se vuelve obvia. ¡No deseche, recicle! No solo necesita una forma de adquirir un recurso del caché, sino también una forma de devolverlo. En ese momento, la memoria caché es propietaria nuevamente y puede optar por conservar el recurso para usarlo en el futuro o para eliminarlo.
public interface IDisposableItemCache<T> : IDisposable
where T:IDisposable
{
/// <summary>
/// Creates a new item, or fetches an available item from the cache.
/// </summary>
/// <remarks>
/// Ownership of the item is transfered from the cache to the client.
/// The client is responsible for either disposing it at some point,
/// or transferring ownership back to the cache with
/// <see cref="Recycle"/>.
/// </remarks>
T AcquireItem();
/// <summary>
/// Transfers ownership of the item back to the cache.
/// </summary>
void Recycle(T item);
}
editar: Me acabo de dar cuenta de que esta idea también existe en Spring, donde se llama pool de objetos . Sus métodos BorrowObject
y ReturnObject
coinciden con los métodos en mi ejemplo.
En primer lugar, el tipo que envuelve los recursos nativos debe ser finalizable, no solo desechable. Aún mejor, use SafeHandle para envolver los recursos nativos.
A menos que alguien sea explícitamente responsable de decir que ya terminaron el artículo y que puede ser eliminado, entonces creo que es mejor que deje que el GC se encargue de él. Sin embargo, tenga en cuenta que debe ser finalizable, de lo contrario, el GC no le dará una segunda mirada.