2009-07-15 15 views
19

Ho alcuni punti in cui l'implementazione di una sorta di cache potrebbe essere utile. Ad esempio, in caso di ricerche di risorse basate su stringhe personalizzate, ricerca di nomi di proprietà mediante reflection o per avere un solo PropertyChangedEventArgs per nome proprietà.C#: Come implementare una cache intelligente

Un semplice esempio di l'ultimo:

public static class Cache 
{ 
    private static Dictionary<string, PropertyChangedEventArgs> cache; 
    static Cache() 
    { 
     cache = new Dictionary<string, PropertyChangedEventArgs>(); 
    } 
    public static PropertyChangedEventArgs GetPropertyChangedEventArgs(
     string propertyName) 
    { 
     if (cache.ContainsKey(propertyName)) 
      return cache[propertyName]; 

     return cache[propertyName] = new PropertyChangedEventArgs(propertyName); 
    } 
} 

Ma, sarà questo lavoro bene? Ad esempio, se avessimo un intero carico di diversi propertyNames, ciò significherebbe che ci ritroveremmo con una cache enorme che non sarebbe mai stata raccolta dalla spazzatura o altro. Sto immaginando se ciò che è memorizzato nella cache sono valori più grandi e se l'applicazione è di lunga durata, questo potrebbe finire come una specie di problema ... o cosa ne pensi? Come dovrebbe essere implementata una buona cache? Questo è abbastanza buono per la maggior parte degli scopi? Qualche esempio di alcune buone implementazioni della cache che non sono troppo difficili da comprendere o troppo complesse da implementare?

+12

"* Ci sono solo due cose difficili in informatica:. Invalidazione della cache e le cose di denominazione *" - Phil Karlton – JulianR

+1

Questo non vale una risposta intera, ma questa particolare implementazione sarebbe fallire in modo spettacolare in un ambiente multi-thread. Al minimo è necessario un blocco per l'accesso a 'cache' in' GetPropertyChangedEventArgs() '. Solo cercando di aiutare chiunque arrivi e copia-incolla questo. – aaronburro

risposta

18

È possibile includere ciascuno degli elementi memorizzati nella cache in un numero WeakReference. Ciò consentirebbe al GC di recuperare gli articoli se e quando richiesto, tuttavia non fornisce alcun controllo granulare su quando gli elementi scompariranno dalla cache o consentire di implementare politiche di scadenza esplicite, ecc.

(Ha Ho appena notato che l'esempio fornito su MSDN page è una semplice classe di memorizzazione nella cache.)

+1

Freddo! Che cosa fa esattamente WeakReference in questo caso? – Svish

+1

Questa pagina ha una buona spiegazione: http://msdn.microsoft.com/en-us/library/ms404247.aspx – LukeH

+4

In pratica, trovo che .NET GC sia così efficiente/aggressivo che i riferimenti deboli non si bloccano in giro a lungo, il che significa che sono adatti solo a dati di breve durata e/o economici da ricreare. – LukeH

3

Questo è un bel dibattito avere, ma a seconda dell'applicazione, ecco alcuni suggerimenti:

è necessario definire la dimensione massima della cache, che cosa fare con i vecchi elementi, se la cache è piena, hanno uno scavenging strategia, determinare un tempo di vita dell'oggetto nella cache, la tua cache può/deve essere persistente da un'altra parte di quella memoria, in caso di interruzione anomala dell'applicazione, ...

+1

Che cos'è una strategia di scavenging? – Svish

+4

L'algoritmo di scavenging è responsabile della rimozione di elementi dalla cache per liberare le risorse di archiviazione della cache. –

+0

Ah, ok. Ciò ha senso. – Svish

24

Questo è un grosso problema, è necessario determinare il dominio del problema e applicare le tecniche corrette. Ad esempio, come descriveresti la scadenza degli oggetti? Diventano stantii in un intervallo di tempo prestabilito? Diventano stantii da un evento esterno? Con che frequenza succede? Inoltre, quanti oggetti hai? Infine, quanto costa generare l'oggetto?

La strategia più semplice sarebbe quella di eseguire la memoizzazione diretta, come sopra. Ciò presuppone che gli oggetti non scadano mai e che non ci siano così tanti da far funzionare la tua memoria asciutta e che pensi che il costo per creare questi oggetti giustifichi l'uso di una cache per cominciare.

Il livello successivo potrebbe essere quello di limitare il numero di oggetti e utilizzare un criterio di scadenza implicito, ad esempio LRU (utilizzato meno di recente). Per fare ciò, in genere si utilizza un elenco doppiamente collegato oltre al dizionario, e ogni volta che si accede a un oggetto viene spostato in primo piano nell'elenco. Quindi, se devi aggiungere un nuovo oggetto, ma supera il limite degli oggetti totali, rimuoveresti dal retro dell'elenco.

Successivamente, potrebbe essere necessario applicare la scadenza esplicita, in base al tempo o ad alcuni stimoli esterni. Ciò richiederebbe di avere una sorta di evento di scadenza che potrebbe essere chiamato.

Come potete vedere, c'è un sacco di design nella memorizzazione nella cache, quindi è necessario comprendere il dominio e l'ingegnere in modo appropriato. Non mi hai fornito dettagli sufficienti per discutere specifiche, mi sentivo.

P.S. Ti preghiamo di considerare l'utilizzo di Generics quando definisci la tua classe in modo da poter archiviare molti tipi di oggetti, consentendo così il riutilizzo del codice di memorizzazione nella cache.

+2

+1 per menzionare molti fattori e suggerire soluzioni di base. –

+0

Ho lasciato delle spesifiche solo perché speravo di avere più consigli generali su cosa pensare ecc. Quindi grazie per questo :) Per l'uso di generici, intendi scambiare Cache con Cache e scambiare tutto il PropertyChangedEventArgs con T pure? O qualcosa di più intelligente? Non tutti gli altri oggetti hanno senso identificarsi con una stringa che penserei ... – Svish

+1

Generici generici di base sì, potresti voler usare lo stesso schema del dizionario e lasciare che l'utente della tua cache determini il tipo di K e V. –

3

Questo è un problema comune che presenta molte soluzioni a seconda delle esigenze dell'applicazione. È così comune che Microsoft ha rilasciato un'intera libreria per affrontarlo. Dovresti controllare Microsoft Velocity prima di caricare la tua cache. http://msdn.microsoft.com/en-us/data/cc655792.aspx Spero che questo aiuto.

+0

+1: questa libreria ha le sue radici nel blocco dell'applicazione Caching dalla libreria aziendale. –

+0

Quale ora è chiamato "Servizio di memorizzazione nella cache di AppFabric". Mancano due parole chiave: portabilità e gratis. Perché Microsoft non può semplicemente rilasciare qualcosa come parte standard di .NET? –

1

È possibile utilizzare uno WeakReference ma se il proprio oggetto non è così grande come non lo sarebbe perché lo WeakReference prenderebbe più memoria dell'oggetto stesso che non è una buona tecnica. Inoltre, se l'oggetto è un utilizzo a breve termine in cui non passerà mai alla generazione 1 dalla generazione 0 sul GC, non c'è molto bisogno che l'interfaccia WeakReference ma IDisposable sull'oggetto abbia la versione su SuppressFinalize.

Se si desidera controllare la durata, è necessario un timer per aggiornare il datetime/timespace di nuovo il desideratoExpirationTime sull'oggetto nella cache.

L'importante è che se l'oggetto è grande, optare per WeakReference altrimenti utilizzare il riferimento forte. Inoltre, è possibile impostare la capacità sul dizionario e creare una coda per richiedere oggetti aggiuntivi nel contenitore temporaneo che serializza l'oggetto e lo carica quando c'è spazio nel dizionario, quindi cancellarlo dalla directory temporanea.

9

Sembra che .NET 4.0 ora supporti System.Runtime.Caching per il caching di molti tipi di cose. Dovresti esaminare prima questo, invece di reinventare la ruota. Maggiori dettagli:

http://msdn.microsoft.com/en-us/library/system.runtime.caching%28VS.100%29.aspx

+4

Tranne che utilizza String come chiave e Oggetto per il valore della mappa! Man che delusione, come possono implementare un'API generica come una cache e non utilizzare i generici per il tipo di chiave/oggetto? – marq

+0

Bene, posso capire usando String per una chiave. Non conosco alcun oggetto hash che non trasponga almeno la chiave in una cosa basata su String. E non è Object solo un contenitore generico? –

+0

@BrendanByrd Ogni oggetto ha già un metodo di codice hash. – Casey

Problemi correlati