2017-05-10 3 views
0

Nehmen wir an, Sie haben ein .net-Objekt bufferObject, das intern eine Art nicht verwalteten Speicherpuffer beträchtlicher Größe verwendet. Es ist so implementiert, dass es seinen zugewiesenen Speicher freigibt, wenn es entsorgt (oder finalisiert) wird.So binden Sie die Lebensdauer eines Objekts an die Lebensdauer eines anderen Objekts mit C#

Sie erstellen dann ein anderes Objekt reusingBufferObject und es mit einem Zeiger auf die internen Puffer von bufferObject liefern. Die beiden Objekte selbst kennen also die Existenz der anderen nicht.

Wenn bufferObject den Gültigkeitsbereich verlässt und entsorgt/finalisiert wird, wird der interne Puffer freigegeben und reusingBufferObject wird auf ungültigen Speicher zugreifen.

Natürlich kann das ganze Problem vermieden werden, indem Sie das Design der zugrunde liegenden Klasse der beiden Objekte ändern und reusingBufferObject Referenz bufferObject lassen. Nehmen wir an, dass beide Objekte von Drittanbieterklassen instanziiert werden und ihr Design nicht geändert werden kann.

Ein Beispielfall wäre die Verwendung einiger Bildverarbeitungsbibliotheken. Ein internes Bildobjekt müsste in ein Bitmap Objekt umgewandelt werden, um es auf dem Bildschirm anzuzeigen, aber aus Leistungsgründen muss der interne Puffer des Bildobjektes vom Objekt Bitmap wiederverwendet werden, anstatt nur dorthin kopiert zu werden.

Ist es möglich, die Lebensdauer von bufferObject auf die Lebensdauer von reusingBufferObject, zu binden, so dass es für die Garbage Collection qualifizierten bekommt erst nach reusingBufferObject tut, ohne manuell den Überblick über die gesamte Lebensdauer zu halten?

EDIT:

Hier ein kurzes Beispiel, das Problem zu veranschaulichen:

namespace Example 
{ 
    // Some third party class with no control over its design. 
    // The Data property is the pointer to an unmanaged memory 
    // buffer. 
    public class ThirdPartyImage : IDisposable 
    { 
     public int Width { get; } 
     public int Height { get; } 
     public int Stride { get; } 
     public PixelFormat Format { get; } 
     public IntPtr Data { get; } 

     public ThirdPartyImage(string filename); 
     public Dispose(); 
    } 

    public static class MyOwnExtensions 
    { 
     // Some method to reuse the internal memory buffer. 
     public static Bitmap ToBitmap(this ThirdPartyImage image) 
     { 
      return new Bitmap(image.Width, image.Height, image.Stride, image.Format, 
       image.Data); 
     } 
    } 

    class Program 
    { 
     static void Main(string[] args) 
     { 
      Bitmap bitmap = Foo(); 

      // ThirdPartyImage is not referenced anymore and the internal buffer (Data) 
      // will be freed when garbage collected. 

      Bar(bitmap); 
     } 

     private static Bitmap Foo() 
     { 
      ThirdPartyImage image = ThirdPartyImage("foo.bmp"); 
      Bitmap bitmap = image.ToBitmap(); 

      return bitmap; 
     } 

     private static void Bar(Bitmap bitmap) 
     { 
      // Do some Bitmap related work here. 
      // The buffer could already be freed though. 
     } 
    } 
} 

EDIT:

Was ich suche ist so etwas wie eine Garbage Collection-Methode, dass können Sie die Lebensdauer eines Objekts mit einem anderen verknüpfen.

oder irgendeine Art von Benachrichtigung, wenn die reusingBufferObject/Bitmap gesammelt werden oder kommen für Sammlung bekommen, so dass ich einige automatisierten Mechanismus bauen kann ein Hilfsobjekt beteiligt, die bufferObject und WeakReference-reusingBufferObject eine starke Referenz hält.

Die manuelle Verfolgung von zwei Objekten, von denen eines eigentlich nicht mehr benötigt wird (außer dem intern verwendeten Puffer), zB durch die Einführung einer Wrapper-Klasse, scheint mir einfach zu kompliziert zu sein.

+0

Es hängt davon ab, welche Klasse 'bufferObject' leitet sich aus. Kannst du auch mehr Details über die beteiligten Arten im Allgemeinen geben? Die Metadatenansicht der Typen für "bufferObject" und "reusingBufferObject" wäre sehr nützlich. –

+1

Es ist nicht möglich, diese Frage ohne weitere Details zu beantworten. Die genaue Semantik, wie die verschiedenen Objekte zugeordnet und zerstört werden, bestimmt, ob dies möglich ist oder nicht. – xxbbcc

+1

Auch wenn 'bufferObject' nicht vor' reusingBufferObject' gesammelt wird, wie soll verhindert werden, dass 'bufferObject.Dispose()' aufgerufen wird? –

Antwort

1

In Ordnung, es scheint, dass ConditionalWeakTable<TKey, TValue> macht was ich will. das Beispiel ein wenig ändern, sollte es funktionieren:

public static class MyOwnExtensions 
{ 
    private static readonly ConditionalWeakTable<Bitmap, ThirdPartyImage> 
     WeakBitmapImageTable = new ConditionalWeakTable<Bitmap, ThirdPartyImage>(); 

    public static Bitmap ToBitmap(this ThirdPartyImage image) 
    { 
     Bitmap bitmap = new Bitmap(image.Width, image.Height, image.Stride, image.Format, 
      image.Data); 
     WeakBitmapImageTable.Add(bitmap, image); 

     return bitmap; 
    } 
} 
1

Wer das Objekt erstellt, ist verantwortlich für deren Entsorgung. Wenn Sie objectA und objectA benötigen objectB, aber beide Objekte werden von einem dritten Objekt objectC erstellt, dann objectC muss sicherstellen, dass beide zu entsorgen.

Das ist der wichtigste Punkt zu erinnern: Der Schöpfer ist verantwortlich für die Entsorgung.

Dies ist eines der größten Probleme mit einigen IoC-Containern: Sie schaffen Dinge, aber wenn es um die Entsorgung geht, sind die Dinge jemand anderen überlassen und das macht die Sache ziemlich schwierig.

0

Der erste Teil Ihrer Frage ist sehr generisch und kann nicht so beantwortet werden wie er ist. Meine Antwort versucht nur, Ihr spezifisches Codebeispiel in der Frage zu beantworten.

Seit ThirdPartyImage implementiert IDisposable, sein erwartetes Verhalten ist, dass es diesen Puffer behält, bis Sie Dispose() darauf aufrufen. Wenn Sie keine Kopie des Image-Puffers erstellen möchten, müssen Sie während der gesamten Lebensdauer Ihrer Bitmap einen Verweis auf ThirdPartyImage behalten. Andernfalls stürzt Ihr Programm irgendwann ab, wenn der Puffer freigegeben wird und die Bitmap nicht mehr gerendert werden kann .

Sie können dies auf vielfältige Weise - eine globale Karte der ThirdPartyImage Instanzen verwenden, durch die Bitmap eingegeben (dies ist schwierig und kann ein Wartungsproblem schnell werden) oder Sie beiden Objekte halten können (ThirdPartyImage und die Bitmap) in einem Klasse, die Sie an jeden Ort weitergeben (das können Sie). Auf diese Weise, wenn Sie bereit sind, die Bitmap loszuwerden, zerstören Sie einfach das Halterobjekt, das Dispose() auf der Bitmap und dann auf ThirdPartyImage aufruft.

Sie können versuchen, Wege zu finden, diese Art von Setup tiefer in einer Struktur zu verstecken, die das scheinbar automatisiert, aber am Ende ein Objekt in ein anderes erreicht und dies verbindet sie miteinander. Nur Sie wissen, wann es sicher ist, das erste Objekt loszuwerden, damit das zweite Objekt auch verschwinden kann.

Verwandte Themen