2009-02-20 17 views
10

Ho un oggetto che è costoso da creare, che utilizza alcune risorse non gestite che devono essere esplicitamente liberate una volta completate e quindi implementare IDisposable(). Vorrei una cache per esempio di queste costose risorse in modo che il costo di creazione fosse ridotto al minimo, ma ho difficoltà a sapere come gestire lo smaltimento.Come gestire gli oggetti IDisposable che sono memorizzati nella cache?

Se i metodi che utilizzano gli oggetti sono responsabili per lo smaltimento, mi ritrovo con istanze disposte nella cache, che devono quindi essere ricreate, annullando il punto della cache. Se non dispongo gli oggetti nei metodi che li usano, non vengono mai eliminati. Ho pensato che potevo eliminarli quando sono stati estratti dalla cache, ma poi potrei finire per smaltire un'istanza che è ancora utilizzata da un metodo.

È valido lasciarli fuori dal campo di applicazione e essere raccolti dal garbage collector e liberare le risorse in quel punto? Questo sembra sbagliato e contro l'idea che siano monouso ...

risposta

4

Gli oggetti monouso devono sempre disporre di un proprietario chiaro che è responsabile dello smaltimento. Tuttavia, questo non è sempre l'oggetto che li ha creati. Inoltre, la proprietà può essere trasferita.

Realizzando ciò, la soluzione diventa ovvia. Non smaltire, riciclare! Non è necessario solo un modo per acquisire una risorsa dalla cache, ma anche un modo per restituirlo. A quel punto la cache è nuovamente proprietaria e può scegliere di conservare la risorsa per uso futuro o di eliminarla.

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

    } 

edit: Ho appena notato che questa idea esiste anche in primavera, in cui si parla di object pool. I loro metodi BorrowObject e ReturnObject corrispondono ai metodi nel mio esempio.

+0

Fondamentalmente ho trovato una soluzione ibrida tra questa e la risposta di NoBugz. Grazie –

2

È possibile scollegare le risorse non gestite dall'istanza gestita e utilizzare un gestore cache per contenere un set di risorse non gestite. L'oggetto gestito proverà ad acquisire un'istanza della risorsa non gestita dal gestore della cache che ne creerà una o ne darà una libera dalla cache e la restituirà al gestore della cache (anziché eliminarla autonomamente) al momento della sua eliminazione . Il gestore della cache sarà l'unico oggetto responsabile per allocare e liberare risorse non gestite quando vede che è necessario.

3

Prima di tutto, il tipo che avvolge le risorse native dovrebbe essere finalizzabile, non solo monouso. Ancora meglio, utilizzare SafeHandle per racchiudere le risorse native.

A meno che qualcuno non sia esplicitamente responsabile per aver detto che è stato fatto con l'articolo e può essere smaltito, allora penso che sia meglio lasciare che il GC si occupi di esso. Nota che deve essere comunque definibile, altrimenti il ​​GC non gli darà una seconda occhiata.

4

A (mis-) citazione Raymond Chen: Ogni della cache senza una politica di scadenza è una perdita di

Quindi, impostare una politica di scadenza della cache chiaro, e lasciare che la cache li smaltire come il caso normale. Ciò lascia comunque l'arresto dell'applicazione per essere gestito.

Se le risorse non gestite sono di proprietà del processo, è possibile consentire al processo di rilasciarle all'arresto.

Se le risorse non gestite sono non di proprietà del processo, è necessario rilevare l'arresto e distribuire esplicitamente gli elementi memorizzati nella cache.

Se non è possibile rilevare in modo affidabile lo spegnimento del processo e le risorse gestite sono costose, quelle non gestite non lo sono, separare la gestione dalle risorse non gestite e lasciare che la cache mantenga solo quelle gestite.

Quando le risorse non gestite sono costose, quindi richiedono il caching e non sono di proprietà del processo e non è possibile rilevare in modo affidabile l'arresto del processo e non è possibile permetterne la fuga, il problema non può essere risolto.

0

Un oggetto deve essere eliminato dalla classe che lo crea. Dal momento che i chiamanti non hanno creato gli elementi nella cache, non hanno alcuna disposizione commerciale.

avrei voluto fare in modo che il vostro metodo factory viene chiamato qualcosa come "Get Classe" piuttosto che "Crea Classe" per sottolineare che il chiamante non è responsabile per la creazione, e quindi non per disposizione.

+0

"creatore è proprietario" è una possibile linea guida per la progettazione, ma come la risposta indica già: non è sempre chiaro chi è il creatore (la fabbrica o il cliente di fabbrica?). È preferibile documentarlo in modo chiaro ed esplicito. Come dimostra la mia risposta, puoi persino trasferire la proprietà. –

1

È possibile risolvere questo con una classe di fabbrica e IDisposable. Ad esempio:

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

Questa soluzione può avere alcuni effetti inaspettati perché distorce il concetto di proprietà. Il codice client è responsabile della chiamata di eliminazione, ma se l'oggetto è mutevole, è necessario tenere presente che potrebbero esserci altri proprietari che lavorano con lo stesso oggetto. –

+1

No, se è modificabile, non dovrebbe essere memorizzato nella cache. –

+1

Questa classe non è protetta da thread - i tuoi inc/dec interbloccati non proteggono le guardie se. –

Problemi correlati