5

我当然知道我不能Graphics从不同的线程中绘制到同一个对象上,但是我不能在不同的线程中绘制到不同的对象上也是真的吗? Graphics

考虑以下控制台程序:

class Program
{
    static ThreadDrawer[] drawers;
    static void Main(string[] args)
    {
        int numThreads = 8;
        drawers = new ThreadDrawer[numThreads];
        for (int i = 0; i < numThreads; i++)
        {
            drawers[i] = new ThreadDrawer();
            drawers[i].Start();
        }
        for (int i = 0; i < numThreads; i++)
        {
            drawers[i].Wait();
        }
        Console.WriteLine("Complete.");
        Console.ReadKey();
    }

    class ThreadDrawer
    {
        private Thread thread;
        private AutoResetEvent resetEvent;

        public ThreadDrawer()
        {
            thread = new Thread(DrawRandomCircles);
            resetEvent = new AutoResetEvent(false);
        }

        public void Start()
        {
            thread.Start();
        }

        public void Wait()
        {
            resetEvent.WaitOne();
        }

        public void DrawRandomCircles()
        {
            Random r = new Random(Environment.TickCount);
            using (Bitmap b = new Bitmap(1000, 1000))
            using (Graphics g = Graphics.FromImage(b))
            {
                for (int i = 0; i < 100000; i++)
                {
                    g.DrawEllipse(Pens.Red, new Rectangle(r.Next(1000), r.Next(1000), 200, 200));
                }
            }
            resetEvent.Set();
        }
    }
}

该程序在每个线程中创建一个Bitmap并继续使用一个Graphics对象在其上绘制随机椭圆,该对象也是每个线程从Bitmap.

由于构建.net2多线程的要求是使用Threads 和AutoResetEvents 而不是 TPL 实现的。

程序执行时不会抛出异常,但它是串行执行的。使用n线程会增加执行时间,n使用任务管理器可以清楚地看到只使用了一个内核。

需要注意的是,这些都与任何 UI 元素无关

这里发生了什么?对象是否Graphics锁定在静态对象上?

4

3 回答 3

5

这是我用来查看这些线程发生了什么的并发分析器的屏幕截图:

在此处输入图像描述

是的,你可以看到很多红色(阻塞)和绿色斑点(执行)。线程轮流进入内部 GpGraphics::RenderDrawPath() 函数中获取的关键部分。较大的绿色斑点是程序实际画线的地方(我用 DrawRectangle 替换了 DrawEllipse 并摆脱了 Random 调用)。

存在一些并发性,例如,您可以看到 RenderDrawPath() 调用与呈现抗锯齿线的代码重叠,总体 cpu 负载约为 35%。但是没有多少。

当然,你无能为力。您可以通过在自己的程序中重叠逻辑来决定使用 GDI+ 调用绘制什么,从而取得成功。这通常会发生,测试过于综合。

于 2013-08-18T12:18:05.523 回答
3

似乎锁定发生在 GDI+ 库内部的非托管代码中(不幸的是,官方文档中没有提到这种行为)。

类似问题:Parallelizing GDI+ Image Resizing .net

于 2013-08-18T12:11:32.247 回答
2

我不是 100% 确定.. 但是是的,类private static中有一个锁定对象Graphics。它似乎只从 中锁定,而每当在对象中初始化GetHalftonePalettea 时,就会调用它。看来这可能是争论的原因。BitmapGraphics

(注:使用 ILSpy 5 分钟后的初步发现.. 不是很深入)

于 2013-08-18T11:40:52.003 回答