C# Picturebox 拉伸(stretch)内存问题

标签 c# picturebox

我知道之前有人问过这个问题,但我的问题直接与在图片框上使用“拉伸(stretch)”(或“正常”以外的任何其他内容)有关,“正常”效果很好。

我有一个简单的应用程序,基本上每秒截屏 60 次 _timer.Interval = (1.0/60.0) * 1000.0; 使用计时器。不幸的是,使用“拉伸(stretch)”会导致使用越来越多的内存,最终导致应用程序崩溃。

public partial class Form1 : Form
{

    System.Timers.Timer _timer = new System.Timers.Timer();

    public Form1()
    {
        InitializeComponent();

        _timer.Elapsed += new ElapsedEventHandler(OnElapsed);
        _timer.Interval = (1.0/60.0) * 1000.0;
    }

    private void btnCapture_Click(object sender, EventArgs e)
    {
       _timer.Enabled = true;
    }

    private void OnElapsed(object o, ElapsedEventArgs e)
    {
        Rectangle bounds = Screen.PrimaryScreen.Bounds;
        Bitmap bmp = new Bitmap(bounds.Width, bounds.Height);

        using (Graphics g = Graphics.FromImage(bmp))
        {
            g.CopyFromScreen(0, 0, 0, 0, new Size(bounds.Width, bounds.Height));

            using (Image prev = pbCapture.Image)
            {
                pbCapture.Image = bmp;
            }
        }
    }
}

如您所见,我处理了图形 IDisposable 和图片框使用的先前图像。但是,内存仍然增加。

我做错了什么吗?

最佳答案

以如此高的速率创建大量大位图通常是一种糟糕的方法。我建议您创建一次缓存位图以减少内存压力。您也可以使用任何 GDI 函数直接复制位图数据。

您的内存泄漏原因是多线程问题。 Windows 上的默认计时器分辨率为 15.6 毫秒。在 60 赫兹(16.67 毫秒)的速率下,很可能至少有两个事件几乎同时被触发。

很明显,在这种情况下,您会遇到竞争条件,因此您的位图将无法正确处理,并且会卡在内存中(在终结器队列中)。由于您的 CPU 负载过重,垃圾收集器无法有效地清除僵尸对象。

要检查这一点,只需在事件处理程序中设置一个简单的 lock 部分,这样一次只有一个线程可以访问位图。您会注意到不会再发生内存泄漏。

您当然可以将计时器分辨率更改为例如1 毫秒。但是你必须保证你的位图处理会在下一个事件到来之前完成。这在 .NET 世界中很难做到。

另一个解决方案是将 System.Timers.Timer 更改为 System.Windows.Forms.Timer。后者将在 GUI 线程而不是线程池线程上引发事件。但您必须记住,所有处理都将在 GUI 线程上完成,这会使您的用户界面无响应。

也许最好的解决方案是实现一些“掉帧”行为。如果一帧到达时前一帧仍在处理中,则只需丢弃新帧。你可以用例如一个信号量

另一种解决方案,如@BradleyUffner 所建议的那样,是使计时器不可重复,并在处理完一帧后启用下一个事件处理程序。

关于C# Picturebox 拉伸(stretch)内存问题,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/46848232/

相关文章:

c# - 编译错误集合被修改

c# - .net DispatcherTimer 重复触发刻度事件

c# - 子类上的 NHibernate (3.1) 子查询未加入基类表

c# - 了解 linq 中使用的 xml 命名空间

c# - 为什么 DrawString 看起来很糟糕?

c++ - 如何将 url 中的图像加载到图片框中?

c# - 如何将图像的一部分插入图片框?

c# - 字符串 .split c# 引发 IndexOutOfRangeException

c# - 如何将生成的位图加载到 PictureBox 中?

c# - 将笛卡尔坐标转换为图片框坐标 c#