c# GDI+,在循环中创建 LinearGradientBrush(内存泄漏)

发布于 2024-08-02 22:56:17 字数 1227 浏览 2 评论 0原文

今天我遇到了一个两难的境地。我创建了一个使用 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(这让我很烦恼),但那是因为我必须这样做。我无法在循环外部创建一个,因为它的构造函数集要求我设置仅在循环内部已知的参数。

我发现在 LinearGradientBrush 对象上使用 dispose 方法并不是那么可靠。如果我运行我的应用程序并在任务管理器中查看它,它会喷出内存。当我添加 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 ?或者有更好的解决方案可以使用吗?

非常感谢

Today I came across a bit of a dilema. I have created an app that uses GDI+ to draw on a form. The drawing is triggered by a timer every second. The draw method uses a for loop to iterate through a collection of objects and if they are of a certain state, draw them.

I want to draw them using a LinearGradientBrush simply because it looks so much nicer than a simple Brush. Have a look at the following

            //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();
        }

I am creating a new LinearGradientBrush for each iteration of the loop (which bothers me), but thats because I have to. I cannot create one outside the loop because its constructor set demands that I set parameters which are only ever known inside the loop.

I find that using the dispose method on the LinearGradientBrush object is not all that reliable. If I run my app and view it in Task manager, its spewing memory. When I then add the b = null line that seems to help hugely as follows

            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;
            }
        }

I am just wondering if there is a better way to work with LinearGradientBrushes ? Or is there a better solution to use ?

Many thanks

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(4

疧_╮線 2024-08-09 22:56:17

我建议使用“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 最终会回收这些内存,所以不用担心。

I would recommend using a "using" statement:

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);
         }
     }
}

However, remember, Dispose() does not free (managed) memory. It just releases the unmanaged resources (which is important, and may include unmanaged memory). The memory will not free until the GC runs, which may not happen during your loop.

However, if the memory pressure gets too high, the garbage collector should run within your loop, and you'll see it drop. This is the way .NET is designed - just accept it, and don't worry. The GC will eventually collect this memory, so its not something to worry about.

圈圈圆圆圈圈 2024-08-09 22:56:17

为每个 LED 添加渐变画笔。
如果您无法将其添加到该类中,那么您可以使用 Dictionary将画笔存放起来,以便轻松取用。
这样,每个 Led 只需要一个画笔,而不是每个循环迭代一个画笔

(此外,在示例代码中,如果 !l.IsLit 则没有必要创建画笔)

Add a gradient brush to each Led.
If you can't add it to that class, then you could use a Dictionary<Led,GradientBrush> to store the brushes in to gain easy access to them.
That way you only need one brush per Led instead of one per loop iteration,

(Also, in your example code, there is no point creating the brush if !l.IsLit)

梦冥 2024-08-09 22:56:17

Dispose 与释放托管内存无关。这完全由 GC 处理,它“在需要时”运行。但是,由于刷子很可能带有手柄,因此您应该将其丢弃。我建议您在 using 块中执行此操作,而不是手动调用 Dispose,因为这将确保即使存在以下情况也会调用 Dispose一个例外。

Dispose has nothing to do with freeing managed memory. That is handled entirely by GC, which runs "when needed". However, since the brush most likely holds a handle, you should dispose it. I would recommend that you do that in a using block instead of manually calling Dispose as this will make sure Dispose is called even in the presence of an exception.

小伙你站住 2024-08-09 22:56:17

如果排列数量有限,您可能只需预先创建所有画笔一次:

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 对象需要循环,这应该有助于减少大量创建/删除操作。

If the number of permutations is limited you might just pre-create all your brushes once:

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

A second option is similar, but to create the brushes as needed;

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
}

The other answers are correct that .NET may allow managed memory usage to balloon as long as it's not harming anything. But this should help cut out a lot of the creating/deleting if there are many Led objects to loop through.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文