【问题标题】:Accessing System.Drawing.Bitmap from GTK# Thread throws Object Currently in use elsewhere exception从 GTK# 线程访问 System.Drawing.Bitmap 会抛出对象当前在其他地方使用异常
【发布时间】:2015-03-04 09:18:17
【问题描述】:

我试图在 GTk# 中使用 system.drawing 来操作图像。我希望 UI 在用户更新文本框后立即更新屏幕上的图像。为了实现这一点,我尝试使用 winforms 中的后台工作人员,它起作用了但是当文本框以更高的速度更新时,应用程序就会卡住而没有错误。

所以我在这里查看了 GTK 中的多线程 http://www.mono-project.com/docs/gui/gtksharp/responsive-applications/ 并创建了一个线程。

void textboxchanged()
{
    Thread thr = new Thread (new ThreadStart (ThreadRoutine));
    thr.Start ();

  }

  static void ThreadRoutine ()
  {
        LargeComputation ();

  }

  static void LargeComputation ()
  {
    image=new Bitmap(backupimage);
   //Long image processing 
  }

当文本框中的输入速度甚至有点快时,它的效果比后台工作人员在其他地方抛出当前正在使用的对象错误image=new Bitmap(backupimage); 效果差。我做错了什么?

更新 1:

我没有使用同时执行 2 个不同操作的 2 个不同线程处理同一图像。我在旧线程完成之前调用执行相同操作的线程。在后台工作人员中,我需要一种方法来检查是否旧线程在启动新线程之前已经完成工作。所以基本上我正在寻找的是一种检查同一线程实例是否存在的方法 正在运行。在 winforms 中我曾经做过if(backgroundworker.isbusy==false) then do stuff

更新 2

性能下降的解决方案

正如@voo 所建议的,替换全局位图有助于解决问题。我所做的不是使用全局位图。我创建了一个全局字符串(文件名)。现在我使用img=new Bitmap(filename)。尝试快速执行,因为我不能出现错误。所以为了更新 GUI,我使用了这里建议的调用mono-project.com/docs/gui/gtksharp/responsive-applications/。事情是没有错误出现并且图像得到更新,但是当打字操作足够快时,就会涉及到等待。性能下降了。后台工作人员不是这种情况。有没有办法提高性能。

在大图处理操作方法的最后我添加了这个来更新GUI

Gtk.Application.Invoke (delegate {

            MemoryStream istream=new MemoryStream();
            img.Save (istream, System.Drawing.Imaging.ImageFormat.Png);
            istream.Position = 0;
            workimagepixbuff = new Gdk.Pixbuf (istream);
            image1.Pixbuf = workimagepixbuff.ScaleSimple (400, 300, Gdk.InterpType.Bilinear);

        });
        // cannot directly convert Bitmap to Pixbuff,so doing this 

【问题讨论】:

  • 我将使用我的水晶球(因为根本不知道 gtk)并说 gtk 就像几乎所有其他 UI 工具包一样不喜欢你从主线程以外的线程更新 GUI线程并提供一些方法来在主线程街道上对操作进行排队,从而完成昂贵的计算。
  • @Voo 我没有在线程中更新 UI。我只是在处理图像。
  • 你能给使用确切的例外吗?我只是对 libgdiplus 和 mono 源进行了 grep-ped,但我找不到对您提供的文本的任何引用。还是您在 Windows 上运行?
  • @fog 我在 Windows 上运行。例外是系统异常-对象当前在其他地方使用,我明天将更新确切的详细信息,我现在无法访问电脑。我认为是System.InvalidOperationException
  • 你是否从多个线程访问同一个图像?

标签: c# multithreading mono gtk gtk#


【解决方案1】:

这里的问题是您同时在两个地方(两个线程)处理图像,而 .Net(GDI 语言)中的图像操作不允许这样做。 因为您没有提供太多信息,所以我只是在这里猜测。

在 GDI 中操作位图图像时,后台有 BitmapData 需要锁定和解锁。这种机制只是使图片在内存中可用于读/写。但是,当您锁定已锁定的 BitmapData 时,AFAIK 会出现类似的异常:System.InvalidOperationException,位图区域已锁定。

对我来说,这听起来像是您遇到了这种错误,但换句话说,因为您没有明确锁定位图数据位。 GDI 只是告诉你:“我必须锁定位图数据位,但我不能,因为该对象已经在其他地方使用(锁定)了。”

这里的解决方案可能是尝试在线程之间同步位图使用(可能涉及位锁定),只要它们可能发生。所以你可能想使用 lock 关键字或类似的机制:

所以尝试如下所示:

private static object _imageLock = new object();
static void LargeComputation ()
  {
     lock(_imageLock)
     {
       image=new Bitmap(backupimage);
       //Long image processing ...
     }
  }

  static void AnotherImageOperationSomewhereElse()
  {
       lock(_imageLock)
       {
          //Another image processing on backupImage or something derived from it...
       }
  }

【讨论】:

  • 感谢您的回答。我没有使用同时执行 2 个不同操作的 2 个不同线程处理同一图像。我在旧线程完成之前调用执行相同操作的线程。作为在后台工作人员中,我需要一种方法来检查旧线程是否在启动新线程之前完成工作。所以基本上我要寻找的是一种方法来检查同一线程的实例是否正在运行。
  • 您似乎在说“新”线程正在运行,而“旧”线程尚未完成。如果是这样,您可能正在两个不同的线程中处理图像。顺便说一句,如果您不想在“新”和“旧”线程之间进行并行(线程)处理,您可能不想在这里使用线程。不过,如果您尝试在“旧”和“新”线程中锁定位图处理部分,我的解决方案应该可以防止引发的异常。这很有趣,因为我今天在我正在开发的项目中遇到了同样的异常,我通过使用 lock 关键字同步线程来解决它;-)
  • 我不明白这个锁机制是如何工作的。图像锁到底是什么锁(imagelock)是做什么的。你看到我的更新#2了吗
  • @techno,我看到了你的更新,我确信你在这里没有走上正轨。我不能在这里给你关于锁定机制的详细解释,但是在 msdn 上简单搜索一下就会给你所有你需要知道的东西。基本上你应该知道图像在内存中是位图的,修改这个内存需要“锁定”相应的内存位置。由于这一要求,Gdi 图像不是“开箱即用”的线程安全:您必须处理这样一个事实,即多个线程可能会尝试锁定内存并且如果内存已被锁定则失败。 lock 关键字来了。
  • lock 关键字将阻止(暂停执行)任何尝试进入 lock {} 部分的线程,而线程已经在运行它。因此,它是线程之间的一种同步机制,也是您在此处防止多个位映射内存“锁定”所需要的。
猜你喜欢
  • 1970-01-01
  • 2011-12-25
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2020-10-14
  • 1970-01-01
  • 2014-05-20
  • 1970-01-01
相关资源
最近更新 更多