2012-02-14 5 views
9

Per alcune delle mie applicazioni winforms ho bisogno di creare un sacco di oggetti GDI + (pennelli, penne, caratteri, ecc.) E utilizzarli più e più volte. Ho creato un Singleton ghetto di caching per realizzare quello che mi serve, ma il codice odore è schiacciante ...Memorizzazione nella cache di oggetti GDI + in un'applicazione winform: ne vale la pena e come farlo correttamente?

public sealed class GraphicsPalette 
{ 
    public static readonly GraphicsPalette Instance = new GraphicsPalette(); 

    static GraphicsPalette() 
    { 
    } 

    private Dictionary<Color, Brush> solidBrushes; 

    //multithreading 
    private object brushLock; 

    private GraphicsPalette() 
    { 
     solidBrushes = new Dictionary<Color, Brush>(); 

     brushLock = new object(); 
    } 

    public Brush GetSolidBrush(Color color, int alpha) 
    { 
     return GetSolidBrush(Color.FromArgb(alpha, color)); 
    } 

    public Brush GetSolidBrush(Color color) 
    { 
     if (!solidBrushes.ContainsKey(color)) 
     { 
      lock (brushLock) 
      { 
       if (!solidBrushes.ContainsKey(color)) 
       { 
        Brush brush = new SolidBrush(color); 
        solidBrushes.Add(color, brush); 
        return brush; 
       } 
      } 
     } 
     return solidBrushes[color]; 
    } 
} 
  1. Esiste un modo migliore per me di riutilizzare questi oggetti GDI +, al contrario di istanziare loro tutto di nuovo ogni volta che OnPaint() viene chiamato?
  2. Gli oggetti GDI + causano una perdita di memoria non gestita una volta terminato il programma o verrà chiamato il finalizzatore per ogni oggetto Brush che a sua volta rilascerà eventuali risorse non gestite?

Mi scuso se si tratta di una ripetizione, ma non ho trovato domande simili.

+0

È possibile testare facilmente qualsiasi miglioramento delle prestazioni modificando il singleton. Aggiungi una bandiera per alternarla tra un singleton e un ... "multi-ton" (ho fatto quella parola in alto). Il multi-ton eliminerà SEMPRE e ricreerà la risorsa. Quindi riporti qui. –

+0

possibile duplicato di [Qual è l'approccio migliore per eliminare Brush in User Control] (http://stackoverflow.com/questions/8253398/what-is-better-approach-to-dispose-brush-in-user-control) –

+0

Non proprio. Quello che sto cercando di scoprire è che, se mi aggrappo a un gruppo di oggetti GDI + (pennelli) e non chiamiamo esplicitamente Dispose() su di essi, ho la garanzia che verranno tutti correttamente finalizzati e disposti automaticamente quando il mio dominio dell'applicazione termina? Se questo non è il caso, allora sto guardando una perdita di memoria non gestita. –

risposta

4

Non ci saranno perdite di memoria ma è meglio rilasciare oggetti GDI + quando ha senso per voi. Ne esistono un numero limitato nel sistema operativo, quindi potresti causare problemi di rendering nelle tue e altre applicazioni. Un'altra cosa da menzionare è l'incapacità degli oggetti GDI + (tipi di carattere, ecc.) Di essere usati da più di 2 thread nello stesso tempo (potrebbero essere lanciate alcune eccezioni difficili da riprodurre). Potresti essere interessato ad alcune misurazioni del tempo di creazione degli oggetti GDI + effettivi rispetto a possibili ritardi di blocco esclusivi. "L'ottimizzazione prematura è la radice di tutti i mali" © Donald Knuth

In realtà funziona per me fare un po 'di cache degli oggetti GDI +: per ciclo di verniciatura. Il codice client potrebbe assomigliare a questo:

class Visual 
{ 
    public void Draw() 
    { 
     using (new GraphicsPalette()) { 
      DrawHeader(); 
      DrawFooter(); 
     } 
    } 

    private void DrawHeader() { 
     var brush = GraphicsPalette.GetSolidBrush(Color.Green); 
     ... 
    } 

    public void DrawFooter() { 
     using (new GraphicsPalette()) { // ensures palette existence; does nothing if there is a palette on the stack 
      var brush = GraphicsPalette.GetSolidBrush(Color.Green); // returns the same brush as in DrawHeader 
      ... 
     } 
    } 
} 

Quindi abbiamo bisogno GraphicsPalette di ignorare la costruzione nidificato e restituire lo stesso pennello per un dato thread. La soluzione suggerita:

public class GraphicsPalette : IDisposable 
{ 
    [ThreadStatic] 
    private static GraphicsPalette _current = null; 
    private readonly Dictionary<Color, SolidBrush> _solidBrushes = new Dictionary<Color, SolidBrush>(); 

    public GraphicsPalette() 
    { 
     if (_current == null) 
      _current = this; 
    } 

    public void Dispose() 
    { 
     if (_current == this) 
      _current = null; 

     foreach (var solidBrush in _solidBrushes.Values) 
      solidBrush.Dispose();    
    } 

    public static SolidBrush GetSolidBrush(Color color) 
    { 
     if (!_current._solidBrushes.ContainsKey(color)) 
      _current._solidBrushes[color] = new SolidBrush(color); 

     return _current._solidBrushes[color]; 
    } 
} 
+0

Buona risposta.In realtà ho considerato la memorizzazione nella cache degli oggetti GDI all'interno del controllo Winforms piuttosto che un singleton. In questo modo, posso agganciare il metodo di controllo del controllo e sbarazzarmi degli oggetti quando il controllo non è più necessario o una particolare forma si sta chiudendo. –

0

Sulla base della mia esperienza con VG.net, non ritengo il caching degli oggetti GDI + è di solito vale la pena, fatta eccezione per le grandi cose come bitmap. Ovviamente è facile da misurare.

+0

Concordo, sulla base di molti anni di esperienza con ERM Diagram/Crainiate Diagram/Open Diagram. Vale sicuramente la pena riutilizzare una o più bitmap e non gettarle via. –

Problemi correlati