C# 正在尝试绘制多线程矩形

C# 正在尝试绘制多线程矩形,c#,multithreading,winforms,C#,Multithreading,Winforms,我试图在一个表单上画3600点,使用一个线程速度非常慢,所以我决定使用4个线程 在我的代码中,我将3600个点分为4个线程,它们应该绘制它。但是由于某些原因,正在抛出ArgumentOutOfRangeException。 我试图调试我的代码,但找不到错误 代码如下: (忽略类3DPoint,它只是一个具有x,y,z值的点。当我绘制它们时,我只使用x,y值。) 绘制点的代码: public Graphics g; //g = this.CreateGraphics() in form1.

我试图在一个表单上画3600点,使用一个线程速度非常慢,所以我决定使用4个线程

在我的代码中,我将3600个点分为4个线程,它们应该绘制它。但是由于某些原因,正在抛出ArgumentOutOfRangeException。 我试图调试我的代码,但找不到错误

代码如下:

(忽略类3DPoint,它只是一个具有x,y,z值的点。当我绘制它们时,我只使用x,y值。)

绘制点的代码:

    public Graphics g; //g = this.CreateGraphics() in form1.Load()
    public void drawrectangle(_3DPoint)
        float xCord = float.Parse(p.x.ToString());
        float yCord = float.Parse(p.y.ToString());
        Brush b = new SolidBrush(Color.White);
        xCord = lsize * xCord + center.X;
        yCord = lsize * yCord + 10 + center.Y;
        g.FillRectangle(b, xCord, yCord, 2, 2);
    }
    public List<_3DPoint[]> multiThreadsdata = new List<_3DPoint[]>();
    public void handlemultithread(_3DPoint[] P)
    {
        g.Clear(Color.Black);
        for (int i = 0; i < multiThreads.Length; i++)
        {
            multiThreadsdata.Add(new _3DPoint[P.Length / multiThreads.Length]);
        }
        for (int i = 0; i < multiThreads.Length; i++)
        {
            for (int j = (P.Length / multiThreads.Length) * (i); j < (P.Length / multiThreads.Length) * (i + 1); j++)
            {
                multiThreadsdata[i][j - ((P.Length / multiThreads.Length) * i)] = new _3DPoint(P[j]);
            }
        }
        for (int i = 0; i < multiThreads.Length; i++)
        {
            multiThreads[i] = new Thread(() => drawPoints(multiThreadsdata[i]));
            multiThreads[i].Start();
        }
    }
    delegate void SetCallBackPoint(_3DPoint location);
    public void drawPoints(_3DPoint[] locations)
    {
        for (int i = 0; i < locations.Length; i++)
        {
            if (this.InvokeRequired)
            {
                SetCallBackPoint e = new SetCallBackPoint(drawrectangle);
                this.Invoke(e, new object[] { locations[i] });
            }
            else
            {
                drawrectangle(locations[i]);
            }
        }
    }
lsize、center只是我想要的对齐点的变量

所有多线程操作代码:

    public Graphics g; //g = this.CreateGraphics() in form1.Load()
    public void drawrectangle(_3DPoint)
        float xCord = float.Parse(p.x.ToString());
        float yCord = float.Parse(p.y.ToString());
        Brush b = new SolidBrush(Color.White);
        xCord = lsize * xCord + center.X;
        yCord = lsize * yCord + 10 + center.Y;
        g.FillRectangle(b, xCord, yCord, 2, 2);
    }
    public List<_3DPoint[]> multiThreadsdata = new List<_3DPoint[]>();
    public void handlemultithread(_3DPoint[] P)
    {
        g.Clear(Color.Black);
        for (int i = 0; i < multiThreads.Length; i++)
        {
            multiThreadsdata.Add(new _3DPoint[P.Length / multiThreads.Length]);
        }
        for (int i = 0; i < multiThreads.Length; i++)
        {
            for (int j = (P.Length / multiThreads.Length) * (i); j < (P.Length / multiThreads.Length) * (i + 1); j++)
            {
                multiThreadsdata[i][j - ((P.Length / multiThreads.Length) * i)] = new _3DPoint(P[j]);
            }
        }
        for (int i = 0; i < multiThreads.Length; i++)
        {
            multiThreads[i] = new Thread(() => drawPoints(multiThreadsdata[i]));
            multiThreads[i].Start();
        }
    }
    delegate void SetCallBackPoint(_3DPoint location);
    public void drawPoints(_3DPoint[] locations)
    {
        for (int i = 0; i < locations.Length; i++)
        {
            if (this.InvokeRequired)
            {
                SetCallBackPoint e = new SetCallBackPoint(drawrectangle);
                this.Invoke(e, new object[] { locations[i] });
            }
            else
            {
                drawrectangle(locations[i]);
            }
        }
    }
我不知道问题出在哪里,我猜多线程有一些问题,因为我只是一个多线程的初学者


非常感谢。

在评论中应用建议时,完全可以在表单上快速绘制3600个矩形

如果这并不能给你足够的时间,你可以考虑在单个背景线程上创建图像,将它们存储在某种缓冲区中,直到它们需要在“代码>图形< <代码> >对象的“代码>油漆/<代码>事件”窗体上绘制。只有在你能预先知道下一帧需要画什么的情况下,这才是可行的

本例使用一个简单的后台工作程序来填充图像。代码中的注释解释了发生了什么

public partial class Form1 : Form
{
    static ConcurrentQueue<Image>  buffer = new ConcurrentQueue<Image>();
    static Random r = new Random();

    public Form1()
    {
        InitializeComponent();
        backgroundWorker1.RunWorkerAsync();
        // this is already a great performance win ...
        DoubleBuffered = true;
    }

    private void Form1_Paint(object sender, PaintEventArgs e)
    {
        Image img =null;
        // get from buffer ..
        if (!buffer.TryDequeue(out img))
        {
            // nothing available
            // direct random
            for (var x = 0; x < e.ClipRectangle.Width; x++)
            {
                for (var y = 0; y < e.ClipRectangle.Height; y++)
                {
                    using (var pen = new Pen(new SolidBrush(Color.FromArgb(r.Next(255), r.Next(255), r.Next(255)))))
                    {
                        e.Graphics.DrawRectangle(pen, x, y, 1, 1);
                    }
                }
            }
        }
        else
        {
            // otherwise Draw the prepared image
            e.Graphics.DrawImage(img,0,0);
            Trace.WriteLine(buffer.Count);
            img.Dispose();
        }
    }

    private void button1_Click(object sender, EventArgs e)
    {
        // force a repaint of the Form
        Invalidate();
    }

    private void backgroundWorker1_DoWork(object sender, DoWorkEventArgs e)
    {
        // as long as the form is not disposed
        while (!IsDisposed)
        {
            // we keep 60 images in memory
            if (buffer.Count < 60)
            {
                // bitmap
                var bmp = new Bitmap(this.Width, this.Height);
                var img = Graphics.FromImage(bmp);
                // draw
                for (int i = 0; i < 3600; i++)
                {
                    using (var pen = new Pen(new SolidBrush(Color.FromArgb(r.Next(255), r.Next(255), r.Next(255)))))
                    {
                        img.DrawRectangle(pen, r.Next(Width),r.Next(Height), r.Next(Width), r.Next(Height));
                    }
                }
                // store the drawing in the buffer
                buffer.Enqueue(bmp);
            }
            else
            {
                // simple and naive way to give other threads a bit of room
                Thread.Sleep(0);
            }
        }
    }
}
公共部分类表单1:表单
{
静态ConcurrentQueue缓冲区=新ConcurrentQueue();
静态随机r=新随机();
公共表格1()
{
初始化组件();
backgroundWorker1.RunWorkerAsync();
//这已经是一个伟大的性能胜利。。。
双缓冲=真;
}
私有void Form1_Paint(对象发送器、PaintEventArgs e)
{
图像img=null;
//从缓冲区获取。。
如果(!buffer.TryDequeue(out img))
{
//没有可用的
//直接随机
对于(var x=0;x

请记住,当您有一个CPU繁忙的进程时,添加更多线程并不会神奇地使您的方法运行得更快。您甚至可能会让情况变得更糟:更多的线程在CPU上争夺时间。

Winforms UI不是多线程的,您也不能同时从多个线程在同一个GDI+表面上绘制。多线程是一种既困难又昂贵的方法——在猜测方法时要非常小心,这是一种获得大量难以跟踪和修复的微妙错误的好方法。更不用说,由于您正在执行
Invoke
操作,因此您会在UI线程上马歇尔每一次回退,在保持实际工作负载100%单线程的同时浪费大量资源。总的来说,代码中存在很多性能问题-多线程不应该是您尝试的第一件事。“this.CreateGraphics”不好,请在内部使用事件绘制和e.Graphics,并激活DoubleBuffered=true。您将看到性能的提高。使用线程使某些东西“更快”会让您整天失望。@MattWilko我将回到单个线程,并尝试使用x。。。关于这方面的建议。我来看看是怎么回事。如果可以避免的话,也不要为每个点创建笔刷。这个解决方案非常有用,但我会将点的数量增加10-15,以使图形更详细。我想是否有一种方法可以将绘图部分分配给GPU,而不是让CPU来完成?注意:我无法预测下一张图片会是什么样子。谢谢。我刚刚检查了你的代码有多快,但还不够快。我可能不得不提到,我需要它画得足够快,如果用户按下一个按钮,它就会改变得足够快,这样看起来就流畅了。我正在屏幕上移动一个3d功能。包括左右向上向下以及在x轴、y轴和z轴上的旋转。有大约10-12个按钮用于移动。好吧,好吧,对于GDI缺乏任何支持的那种东西,System.Drawing.Successive中没有魔法。我会把你的答案标记为正确的,因为这是一个相当好的性能升级。非常感谢。