multithreading系统.Windows.Graphics
我当然知道我不能从不同的线程绘制到同一个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
,因此使用Thread
和AutoResetEvent
而不是TPL实现multithreading。
程序执行时不会抛出exception,但它会串行执行 。 使用n
线程将执行时间乘以n
,很明显看到使用任务管理器只使用了一个核心。
重要的是要注意, 这些都不与任何UI元素相关联 。
这里发生了什么? Graphics
对象是否锁定在静态对象上?
这是我用来看看这些线程发生了什么的并发分析器的屏幕截图:
是的,你可以看到许多带有绿色斑点(执行)的红色(阻挡)。 线程轮流进入在内部GpGraphics :: RenderDrawPath()函数内部获取的临界区。 较大的绿色斑点是程序实际绘制线条的地方(我用DrawRectangle替换DrawEllipse并摆脱了随机调用)。
有一些并发性,例如,您可以看到RenderDrawPath()调用与呈现消除锯齿线的代码重叠,整体cpu负载约为35%。 但是没有多少。
当然,你无能为力。 您可以通过重叠自己程序中的逻辑来决定使用GDI +调用绘制什么。 这通常会发生,测试过于合成。
似乎锁定发生在非托管代码中,在GDI +库中(不幸的是,这种行为在官方文档中没有提到)。
类似的问题: 并行化GDI +图像大小调整.net
我不是100%确定..但是,是的, Graphics
类中有一个private static
锁定对象。 它似乎只是从GetHalftonePalette
锁定,而GetHalftonePalette
只是在Graphics
对象中初始化Bitmap
被调用。 看起来这可能是争用的原因。
(注意:使用ILSpy 5分钟后的初步调查结果..不是很深入)