Graphics Object目前正在其他地方使用(尝试多线程绘图基元)

时间:2013-12-03 11:57:20

标签: c# .net multithreading graphics

我要谈的代码是为我自己编写的一个程序中较小的一部分来测试我正在开发的库,所以我确信它不是最优的,我不打算改进它因为......好吧,它不值得,因为它只是一个个人测试工具。

代码将灰度图像(存储在byte [,]中)输出到面板。

Graphics g = pImage.CreateGraphics()
int left, right, top, bottom;
left = Math.Max(-pImage.Left, 0);
top = Math.Max(-pImage.Top, 0);
right = Math.Min(pContainer.Width - pImage.Left, Layer.Width);
bottom = Math.Min(pContainer.Height - pImage.Top, Layer.Height);
Brush[] currentBrushCatalogue = DimLights ? brushCatalogueDimmed : brushCatalogueNormal;

for (int i = left - left % ImageResolution; i < right - right % ImageResolution; i += ImageResolution)
  for (int j = top - top % ImageResolution; j < bottom - bottom % ImageResolution; j += ImageResolution)
    g.FillRectangle(currentBrushCatalogue[Layer.GetBrightness(i, j)], new Rectangle(i, j, ImageResolution, ImageResolution));

在这里,

  • pImage是专家组,
  • Layer.GetBrightness从Layer
  • 中的byte [,]获取值
  • brushCatalogue只是一个将字节转换为Brush对象的画笔数组
  • ImageResolution是加快处理速度的方法(为不太详细的图像绘制更少的像素)

现在,我决定让它平行。由于您无法从多个线程使用Graphics对象,因此我决定将其绘制到单独的位图中,然后将所有内容混合在一起。因此我制作了这段代码:

Graphics g = pImage.CreateGraphics()
int left, right, top, bottom;
left = Math.Max(-pImage.Left, 0);
top = Math.Max(-pImage.Top, 0);
right = Math.Min(pContainer.Width - pImage.Left, Layer.Width);
bottom = Math.Min(pContainer.Height - pImage.Top, Layer.Height);
Brush[] currentBrushCatalogue = DimLights ? brushCatalogueDimmed : brushCatalogueNormal;
Bitmap[] bitmaps = new Bitmap[8];

Parallel.For(0, 8, (n) =>
                    {
                        int l = left + (right - left) * n / 8;
                        int r = left + (right - left) * (n+1) / 8;

                        bitmaps[n] = new Bitmap(r - l, bottom - top);
                        Graphics localG = Graphics.FromImage(bitmaps[n]);

                        for (int i = l - l % ImageResolution; i < r - r % ImageResolution; i += ImageResolution)
                            for (int j = top - top % ImageResolution; j < bottom - bottom % ImageResolution; j += ImageResolution)
                                localG.FillRectangle(currentBrushCatalogue[Layer.GetBrightness(i, j)], new Rectangle(i, j, ImageResolution, ImageResolution));

                    }
                    );
for (int n=0;n<8;n++)
  g.DrawImageUnscaled(bitmaps[n],left + (right-left)*n/8,top);

问题是,我仍然收到错误!在我尝试FillRectangle的地方,我得到“对象目前正在其他地方使用”。错误。

有人可以向我解释我做错了什么吗?我假设我在函数内部创建的任何内容都传递给Parallel.For是实例,意思是,每个线程都有自己的l,r和localG副本,每个副本都使用自己的Graphics对象。因为很明显,它根本不起作用!那么为什么“Object目前在其他地方使用”呢?如果没有其他线程接触到localG当前如何在其他地方使用?...

1 个答案:

答案 0 :(得分:2)

您已为每个子任务正确设置了单独的Bitmap / Graphics对象,但currentBrushCatalogue中的画笔仍然是共享的。画笔不是线程安全的,因此您需要为每个子任务创建currentBrushCatalogue的深层副本:

Parallel.For(0, 8, (n) =>
{
    var palette = currentBrushCatalogue.Select( x => x.Clone() ).Cast<Brush>().ToArray();

    // use palette instead of currentBrushCatalogue below

    int l = left + (right - left) * n / 8;
    int r = left + (right - left) * (n+1) / 8;

    // ...