C#正在更新PictureBox图像

时间:2016-07-07 20:41:19

标签: c# multithreading sockets thread-safety

我正在进行一个小屏幕共享项目,并且在主循环中(在第二个线程上)我从连接的套接字读取数据,将其处理为一个小图像(一种块,{{ 1}}和X属性指示绘制该块的位置),并且需要在上一个图像上重绘它 - 它必须非常快速地更新(至少这是我的目标)。

到目前为止,我从初始图像创建了Y,并使用Graphics方法在当前图像上绘制了较小的块(从套接字中不断收到)。

这就是它的样子:

Graphics.DrawImage

我做了一些基准测试,我发现 private void MainScreenThread() { ReadData(); initial = bufferToJpeg(); while (true) { int pos = ReadData(); x = BlockX(); y = BlockY(); Bitmap block = bufferToJpeg(); Draw(block,new Point(x,y)); this.Invoke(new Action(() => pictureBox1.Refresh())); } } private void pictureBox1_Paint(object sender, PaintEventArgs e) { lock (initial) { e.Graphics.DrawImage(initial, 0, 0); } } 方法工作得很慢。

绘制800X500图像的平均时间 ~520ms

我决定自己实现一个更快的方法,使用不安全的指针来加快访问速度。

DrawImage

现在这个电话看起来像这样:

 private  unsafe void Draw(Bitmap bmp2, Point point)
    {
        lock (initial)
        {
            BitmapData bmData = initial.LockBits(new Rectangle(0, 0, initial.Width, initial.Height), System.Drawing.Imaging.ImageLockMode.WriteOnly, initial.PixelFormat);
            BitmapData bmData2 = bmp2.LockBits(new Rectangle(0, 0, bmp2.Width, bmp2.Height), System.Drawing.Imaging.ImageLockMode.ReadOnly, bmp2.PixelFormat);
            IntPtr scan0 = bmData.Scan0;
            IntPtr scan02 = bmData2.Scan0;
            int stride = bmData.Stride;
            int stride2 = bmData2.Stride;
            int Width = bmp2.Width;
            int Height = bmp2.Height;
            int X = point.X;
            int Y = point.Y;

            for (int y = 0; y < Height; y++)
            {
                byte* p = (byte*)scan0.ToPointer();
                p += (Y + y) * stride + X * 4;//setting pointer according the smaller bitmap bounds.
                byte* p2 = (byte*)scan02.ToPointer();
                p2 += stride2 * y;

                for (int x = 0; x < Width; x++)
                {
                    p[0] = p2[0];//B
                    p[1] = p2[1];//G
                    p[2] = p2[2];//R
                    p += 4;//advance pointer +4
                    p2 += 4;//advance pointer +4
                }

            }
            initial.UnlockBits(bmData);
            bmp2.UnlockBits(bmData2);
        }
    }

但我一直在

  

对象目前正在其他地方使用

当我锁定初始图像时......在这一行

   while (true)
        {
            int pos = ReadData();
             x = BlockX();
            y = BlockY();
            Bitmap block = bufferToJpeg();
           Draw(block,new Point(x,y));
            this.Invoke(new Action(() => pictureBox1.Refresh()));         
        }

我不确定为什么会发生这种情况......我不会从任何其他来源访问 BitmapData bmData = initial.LockBits(new Rectangle(0, 0, initial.Width, initial.Height), System.Drawing.Imaging.ImageLockMode.WriteOnly, initial.PixelFormat); ...

我将不胜感激。

感谢。

1 个答案:

答案 0 :(得分:1)

您正在从其他来源访问initial:即由于您的pictureBox1.Image = initial

而负责绘制pictureBox的主GUI线程

我的建议。摆脱pictureBox1.Image = initial位,并向pictureBox添加一个OnPaint处理程序以手动绘制图像。然后围绕资源的使用添加一些简单的锁定代码,例如在后台线程中,例如:

lock (initial)
{
    BitmapData bmData = initial.LockBits(new Rectangle(0, 0, initial.Width, initial.Height), System.Drawing.Imaging.ImageLockMode.WriteOnly, initial.PixelFormat);
    // ....
    initial.UnlockBits(bmData);
}

然后在你的OnPaint处理程序中,执行以下操作:

lock (initial)
{
    e.Graphics.DrawImage(initial, 0, 0);
}

这将防止两个线程在尝试访问同一资源时发生冲突。