2009-02-20 18 views
10

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.¿Cómo administrar objetos IDisposable que se almacenan en caché?

Si los métodos que usan los objetos son responsables de la eliminación, entonces termino con instancias eliminadas en la memoria caché, que luego tienen que ser recreadas, derrotando el punto de la memoria 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 salir 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 ...

Respuesta

4

objetos desechables siempre necesita tener un dueño claro quién es el responsable de la eliminación de ellos. 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 evidente. ¡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); 

    } 

edición: Acabo de notar que esta idea también existe en primavera, donde se le llama un object pool. Sus BorrowObject y ReturnObject métodos coinciden con los métodos en mi ejemplo.

+0

básicamente que terminó con una solución que era un híbrido de esto y respuesta nobugz. Gracias –

2

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.

3

Primero que nada, 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 haber dicho que han terminado con el artículo y que puede desecharse, 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.

4

A (mal) cita Raymond Chen: Cada caché sin una directiva de caducidad es una fuga

lo tanto, establecer una política clara caducidad de la caché, y dejar que el caché de ellas disponer 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 apagar.

Si los recursos no gestionados son no propiedad del proceso, debe detectar el cierre y explícitamente Eliminar los elementos en caché.

Si no puede detectar el cierre del proceso de manera confiable, y los recursos administrados son caros, los no administrados no lo son, separa los recursos administrados de los no administrados, y deja que el caché conserve solamente los gestionados.

Cuando los recursos no administrados son caros, por lo que necesitan almacenamiento en caché, no son propiedad del proceso y no se puede detectar el cierre del proceso confiablemente y no puede permitirse filtrarlos, entonces su problema no se puede resolver.

0

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 asegurarse de que su método de fábrica se llama algo así como "Obtener Clase" en lugar de "Crear Clase" con el fin de hacer hincapié en que la persona que llama no es responsable de la creación, y por lo tanto no para disposición.

+0

"creador es dueño" es una posible guía de diseño, pero a medida que su respuesta ya indica: no siempre es claro quién es el creador (la fábrica o el cliente de la fábrica?). Es preferible documentar esto de manera clara y explícita. Como muestra mi propia respuesta, incluso puedes transferir la propiedad. –

1

Puede resolver esto con un generador de clases y 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 
     // 
    } 
    } 
} 
+0

Esta solución puede tener algunos efectos inesperados porque sesga el concepto de propiedad. El código de cliente es responsable de llamar a disponer, pero si el objeto es mutable también debe tener en cuenta que puede haber otros propietarios trabajando con el mismo objeto. –

+1

No, si es mutable, entonces no se debe almacenar en caché. –

+1

Esta clase no es multi-hilo - el enclavamiento inc/dec de no protegen el caso de los guardias. –

Cuestiones relacionadas