2009-02-20 6 views
10

Ich habe ein Objekt, das teuer zu erstellen ist, die einige nicht verwaltete Ressourcen verwendet, die explizit freigegeben werden müssen, wenn Sie fertig sind und IDisposable() implementieren. Ich möchte zum Beispiel einen Cache dieser teuren Ressourcen, so dass die Erstellungskosten minimiert werden, aber ich habe Probleme zu wissen, wie ich mit der Entsorgung umgehen soll.Wie IDisposable Objekte verwalten, die zwischengespeichert werden?

Wenn die Methoden, die die Objekte verwenden, für die Beseitigung verantwortlich sind, dann lande ich mit entsorgten Instanzen im Cache, die dann neu erstellt werden müssen und den Punkt im Cache besiegen. Wenn ich die Objekte nicht in den Methoden enthalte, die sie verwenden, werden sie niemals entsorgt. Ich dachte, ich könnte sie entsorgen, wenn sie aus dem Cache entfernt werden, aber dann könnte ich am Ende eine Instanz bereitstellen, die immer noch von einer Methode verwendet wird.

Ist es zulässig, sie einfach außerhalb des Geltungsbereichs zu lassen und vom Garbage Collector gesammelt zu werden und die Ressourcen an diesem Punkt freizugeben? Dies fühlt sich falsch an und ist gegen die Idee, dass sie wegwerfbar sind ...

Antwort

4

Einwegartikel müssen immer einen eindeutigen Eigentümer haben, der für die Entsorgung verantwortlich ist. Dies ist jedoch nicht immer das Objekt, das sie erstellt hat. Darüber hinaus kann das Eigentum übertragen werden.

Wenn Sie dies erkennen, wird die Lösung offensichtlich. Nicht entsorgen, recyceln! Sie benötigen nicht nur eine Möglichkeit, eine Ressource aus dem Cache zu erhalten, sondern auch eine Möglichkeit, sie zurückzugeben. An diesem Punkt ist der Cache wieder Eigentümer und kann entscheiden, die Ressource für zukünftige Verwendung zu behalten oder sie zu entsorgen.

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: Ich habe gerade bemerkt, dass diese Idee existiert auch im Frühjahr, wo es ein object pool genannt wird. Ihre Methoden BorrowObject und ReturnObject entsprechen den Methoden in meinem Beispiel.

+0

Ich endete im Grunde mit einer Lösung, die eine Mischung aus dieser und NoBugz Antwort war. Vielen Dank –

2

Sie könnten die nicht verwalteten Ressourcen von der verwalteten Instanz entkoppeln und einen Cache-Manager verwenden, um eine Gruppe nicht verwalteter Ressourcen zu speichern. Das verwaltete Objekt versucht, vom Cache-Manager eine Instanz der nicht verwalteten Ressource zu erhalten, die entweder eine neue Instanz erstellt oder eine freie Instanz aus dem Cache gibt und sie zum Zeitpunkt ihrer Beseitigung an den Cache-Manager zurückgibt (anstatt sie selbst zu verwerfen) . Der Cache-Manager ist das einzige verantwortliche Objekt zum Zuweisen und Freigeben nicht verwalteter Ressourcen, wenn er es für notwendig erachtet.

3

Zunächst sollte der Typ, der die nativen Ressourcen umschließt, finalisierbar sein und nicht nur verfügbar sein. Noch besser: Verwenden Sie SafeHandle, um die nativen Ressourcen zu umbrechen.

Wenn nicht jemand explizit dafür verantwortlich ist zu sagen, dass sie mit dem Artikel fertig sind und entsorgt werden können, dann denke ich, dass es besser ist, dass der GC sich darum kümmert. Beachten Sie, dass es jedoch finalisierbar sein muss, sonst wird der GC keinen zweiten Blick darauf werfen.

4

To (Fehl-) Zitat Raymond Chen: Jeder Cache ohne Ablaufrichtlinie ist ein Leck

also eine klare Cache Ablaufrichtlinie festgelegt, und lassen Sie den Cache, sie als Normalfall verfügen. Dies lässt immer noch das Herunterfahren der Anwendung zu.

Wenn Ihre nicht verwalteten Ressourcen dem Prozess gehören, können Sie sie vom Prozess beim Herunterfahren freigeben lassen.

Wenn die nicht verwalteten Ressourcen nicht im Besitz des Prozesses sind, müssen Sie das Herunterfahren erkennen und die zwischengespeicherten Elemente explizit entsorgen.

Wenn Sie den Prozessabbruch nicht zuverlässig erkennen können und die verwalteten Ressourcen teuer sind, sind die nicht verwalteten nicht, trennen Sie die verwalteten von den nicht verwalteten Ressourcen und lassen Sie den Cache nur die verwalteten beibehalten.

Wenn die nicht verwalteten Ressourcen teuer sind, sie müssen zwischengespeichert werden, und sie sind nicht im Besitz des Prozesses, und Sie können Prozessabschaltung nicht zuverlässig erkennen und Sie können nicht leisten, sie zu verlieren, dann kann Ihr Problem nicht gelöst werden.

0

Ein Objekt sollte von der Klasse entsorgt werden, die es erstellt. Da Ihre Anrufer die Objekte im Cache nicht erstellt haben, haben sie auch nichts damit zu tun.

Ich mag würde sicherstellen, dass Ihre Factory-Methode wie etwas genannt wird, statt, damit der Anrufer nicht verantwortlich zu betonen „Klasse erstellen“ „Klasse Get“ für die Schöpfung ist, und daher nicht für Verfügung.

+0

"creator is owner" ist eine mögliche Design-Richtlinie, aber wie Ihre Antwort bereits zeigt: Es ist nicht immer klar, wer der Schöpfer (der Fabrik oder der Fabrik-Client?) Ist. Es ist vorzuziehen, dies klar und deutlich zu dokumentieren. Wie meine eigene Antwort zeigt, können Sie sogar Eigentumsrechte übertragen. –

1

Sie können dies mit einer Klassenfabrik und IDisposable lösen. Zum Beispiel:

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

Diese Lösung kann einige unerwartete Auswirkungen haben, da sie das Eigentumskonzept verzerrt. Der Client-Code ist verantwortlich für den Aufruf von dispose, aber wenn das Objekt veränderbar ist, muss es auch beachten, dass es andere Besitzer gibt, die mit demselben Objekt arbeiten. –

+1

Nein, wenn es veränderbar ist, sollte es nicht zwischengespeichert werden. –

+1

Diese Klasse ist nicht threadsicher - Ihre verriegelten Inc/Dec's schützen die if-Wachen nicht. –