c # GDI +, Создание LinearGradientBrush в цикле (утечки памяти) - PullRequest
1 голос
/ 26 августа 2009

Сегодня я столкнулся с некоторой дилеммой. Я создал приложение, которое использует GDI + для рисования на форме. Розыгрыш запускается таймером каждую секунду. Метод draw использует цикл for для перебора коллекции объектов и, если они находятся в определенном состоянии, рисует их.

Я хочу нарисовать их с помощью LinearGradientBrush просто потому, что это выглядит намного приятнее, чем простая кисть. Посмотрите на следующее

            //minutes
        foreach (Led l in MinuteGrid.Leds)
        {
            LinearGradientBrush b = new LinearGradientBrush
                (l.LedRectangle, Color.GreenYellow, Color.Green, 110);

            if (l.IsLit)
                g.FillRectangle(b, l.LedRectangle);

            b.Dispose();
        }

Я создаю новый LinearGradientBrush для каждой итерации цикла (что меня беспокоит), но это потому, что я должен. Я не могу создать один вне цикла, потому что его конструктор требует, чтобы я установил параметры, которые только когда-либо известны внутри цикла.

Я считаю, что использование метода dispose для объекта LinearGradientBrush не так уж и надежно. Если я запускаю свое приложение и просматриваю его в диспетчере задач, его извергается память. Когда я тогда добавляю строку b = null, которая, кажется, очень помогает, как показано ниже

            foreach (Led l in MinuteGrid.Leds)
        {
            LinearGradientBrush b = new LinearGradientBrush
                (l.LedRectangle, Color.GreenYellow, Color.Green, 110);

            if (l.IsLit)
                g.FillRectangle(b, l.LedRectangle);

            if (b != null)
            {
                b.Dispose();
                b = null;
            }
        }

Мне просто интересно, есть ли лучший способ работы с LinearGradientBrushes? Или есть лучшее решение для использования?

Большое спасибо

Ответы [ 4 ]

6 голосов
/ 26 августа 2009

Я бы рекомендовал использовать выражение "using":

foreach (Led l in MinuteGrid.Leds)
{
     if (l.IsLit)
     {
         using(LinearGradientBrush b = new LinearGradientBrush(l.LedRectangle, Color.GreenYellow, Color.Green, 110))
         {
            g.FillRectangle(b, l.LedRectangle);
         }
     }
}

Однако помните, что Dispose () не освобождает (управляемую) память . Он просто освобождает неуправляемые ресурсы (что важно, и может включать неуправляемую память). Память не освободится до тех пор, пока не запустится GC, что может не произойти во время цикла.

Однако, если давление памяти становится слишком высоким, сборщик мусора должен работать в вашем цикле, и вы увидите, что он падает. Так устроен .NET - просто примите это и не волнуйтесь. GC в конечном итоге соберет эту память, так что не о чем беспокоиться.

0 голосов
/ 26 августа 2009

Если количество перестановок ограничено, вы можете просто предварительно создать все свои кисти один раз:

LinearGradientBrush rectGreenBrush = new LinearGradientBrush(l.LedRect........);
LinearGradientBrush rectRedBrush = new LinearGradientBrush(l.LedRect........);

foreach (Led l in MinuteGrid.Leds)
{
  LinearGradientBrush b = null;
  if (xxx)
    b = rectGreenBrush;
  else if (yyyy)
    b = rectRedBrush;
  else.....


  do painting
}

cleanup brushes

Второй вариант аналогичен, но для создания кистей по мере необходимости;

List<LinearGradientBrush> createdBrushes = new List<LinearGradientBrush>();

foreach (Led l in MinuteGrid.Leds)
{
  LinearGradientBrush b = null;

  b = FindOrCreateBrushBasedOnLed(l, createdBrushes); 
  // if not already created, creates the brush and adds it to the list

  do painting
}

foreach (LinearGradientBrush b in createdBrushes)
{
  cleanup brushes
}

Другие ответы верны, что .NET может разрешить использование управляемой памяти, если это ничего не вредит. Но это должно помочь исключить большую часть создания / удаления, если есть много объектов Led для циклического прохождения.

0 голосов
/ 26 августа 2009

Dispose не имеет никакого отношения к освобождению управляемой памяти. Это полностью обрабатывается GC, который запускается «при необходимости». Однако, поскольку кисть, скорее всего, держит ручку, вы должны утилизировать ее. Я бы порекомендовал вам делать это в блоке using вместо ручного вызова Dispose, поскольку это обеспечит вызов Dispose даже при наличии исключения.

0 голосов
/ 26 августа 2009

Добавьте градиентную кисть для каждого светодиода. Если вы не можете добавить его в этот класс, вы можете использовать Dictionary , чтобы сохранить кисти, чтобы получить к ним легкий доступ. Таким образом, вам понадобится только одна кисть для Led вместо одной для каждой итерации цикла,

(Также, в вашем примере кода, нет смысла создавать кисть, если! L.IsLit)

...