位图保存时出现“对象正在其他地方使用”的线程问题

3

我有一些类似于这样的代码:

    public void SaveImage(int Counter)
    {
        var task = Task.Factory.StartNew(() =>
        {
            var image = FinalImage;
            if (image != null)
            {
                image.Save(FinalImageSaveLocation + "test" + Counter + ".bmp");
            }
        }, TaskCreationOptions.PreferFairness);
    }

我有一个for循环使用类似下面的代码创建x张图片:

for(int i = 0; i < 100; i++)
{
  Pencil.DrawImage(image, x, y); //Pencil is created at a initialisation stage
  SaveImage(i);                  //by Pencil = Graphics.FromImage(FinalImage);
}

我认为将SaveImage方法作为任务进行处理可以加快速度,但我猜测之所以出现异常是因为循环的下一次迭代正在尝试向最终图像对象进行绘制,而同时正在进行保存。我猜我可以使用锁,但我担心这会减慢速度?是否有解决方法或者我应该只是删除此任务?

3
Bitmap类不是线程安全的,因此您不能同时从多个线程访问它。如果这意味着您必须锁定以防止其被修改,或者制作一个副本以便在自己的时间内保存,那么您只需要选择一个解决方案并使用它,但绝不能从多个线程访问它。您还提到,您在保存时正在对其进行绘制。您是在保存渲染图像吗?如果是的话,您可能需要使用锁或队列,否则您可能会启动任务比它们能够完成得更快。 - Lasse V. Karlsen
2个回答

3

实际上,您无法同时从多个线程访问图像。您需要进行一些同步。如果性能是一个问题,您可以执行以下技巧:

在保存方法中,锁定图像。保存到内存流中,释放锁定,最后保存到磁盘中。(因为磁盘IO非常慢)。

锁定部分仅在需要实际同步时才有用。由于Bitmap不是线程安全的,您不应该首先使用多个线程访问它,因此同步不应该是一个问题。


2
在不同时进行将绘制结果保存到位图中是可以的,只要你不同时进行即可。GDI+包含一项检查,用于验证您是否同时从多个线程访问位图,这就是为什么会出现异常的原因。
一个简单的解决方法是在开始绘制之前创建一个位图,在保存后的任务中释放它。但是,您必须仔细编写代码,如果保存位图所需时间比绘制更长,则仍会出现问题,会导致内存不足。一个信号量可以解决这个问题,将其初始化为您舒适的位图数量。这取决于位图的大小。然后在绘制方法中调用WaitOne(),在保存方法中调用Release()即可。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接