【问题标题】:InvalidOperationException - object is currently in use elsewhereInvalidOperationException - 对象当前正在其他地方使用
【发布时间】:2020-10-14 17:30:47
【问题描述】:

我已经通过this SO question,但它没有帮助。

这里的情况不同。我正在使用后台工作人员。第一个后台工作人员开始对用户的图像输入和 firstbackgroundworker_runworkercompleted() 内部进行操作,我正在使用调用其他 3 个后台工作人员

 algo1backgroundworker.RunWorkerAsync();
 algo2backgroundworker.RunWorkerAsync();
 algo3backgroundworker.RunWorkerAsync();

这是每个的代码:

algo1backgroundworker_DoWork()
{
 Image img = this.picturebox.Image;
 imgclone = img.clone();
 //operate on imgclone and output it
}

algo2backgroundworker_DoWork()
{
 Image img = this.picturebox.Image;
 imgclone = img.clone();
 //operate on imgclone and output it
}

类似的操作在其他 algo*backgrougrondworker_doWork() 中完成。

现在有时我会收到“InvalidOperationException - 对象当前正在其他地方使用”。它非常随意。我有时在 algo1backgroundworker_DoWork 中得到这个,有时在 algo2backgroundworker_DoWork 中得到这个,有时在 Application.Run(new myWindowsForm());

我不知道发生了什么。

【问题讨论】:

    标签: c# .net winforms backgroundworker


    【解决方案1】:

    GDI+ 内部有一个锁,可以防止两个线程同时访问位图。这不是一种阻塞式的锁,它是一种“程序员做错了,我会抛出异常”的锁。您的线程正在轰炸,因为您正在所有线程中克隆图像(== 访问位图)。您的 UI 线程正在轰炸,因为它试图在一个线程克隆它的同时绘制位图(== 访问位图)。

    您需要将对位图的访问限制为仅一个线程。在启动 BGW 之前在 UI 线程中克隆图像,每个 BGW 都需要自己的图像副本。在 RunWorkerCompleted 事件中更新 PB 的 Image 属性。这样你会失去一些并发性,但这是不可避免的。

    【讨论】:

      【解决方案2】:

      因此,看起来您的 BackgroundWorkers 正在尝试同时访问相同的 Windows 窗体组件。这可以解释为什么失败是随机的。

      您需要使用lock 确保不会发生这种情况,可能像这样:

      private object lockObject = new object();
      
      algo1backgroundworker_DoWork()
      {
          Image imgclone;
          lock (lockObject)
          {
              Image img = this.picturebox.Image;
              imgclone = img.clone();
          }
      
          //operate on imgclone and output it
      }
      

      请注意,我确保 imgclone 对于此方法是本地的 - 您绝对不想在所有方法中共享它!

      另一方面,所有方法都使用相同的 lockObject 实例。当 BackgroundWorker 方法进入其lock{} 部分时,其他到达该点的方法将被阻止。因此,确保锁定部分中的代码快速非常重要。

      当您“输出”处理后的图像时,也要小心确保您不会对 UI 进行跨线程更新。检查this post 以获得避免这种情况的巧妙方法。

      【讨论】:

        【解决方案3】:

        在 Windows 窗体中,您不仅应该只从单个线程访问控件,而且该线程应该是主应用程序线程,即创建控件的线程。

        这意味着在 DoWork 中您不应访问任何控件(不使用 Control.Invoke)。所以在这里你会调用 RunWorkerAsync 传入你的图像克隆。在 DoWork 事件处理程序中,您可以从 DoWorkEventArgs.Argument 中提取参数。

        只有 ProgressChanged 和 RunWorkerCompleted 事件处理程序应该与 GUI 交互。

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 2010-11-06
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          相关资源
          最近更新 更多