InvalidOperationException - 对象当前正在其他地方使用

时间:2009-12-05 06:04:42

标签: c# .net winforms backgroundworker

我经历过this SO question,但没有帮助。

这里的情况有所不同。我正在使用Backgroundworkers。第一个backgroundworker开始操作用户的图像输入和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())中获取;

我不知道发生了什么事。

4 个答案:

答案 0 :(得分:45)

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

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

答案 1 :(得分:21)

因此看起来您的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以获得一种简洁的方法来避免这种情况。

答案 2 :(得分:1)

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

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

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

答案 3 :(得分:0)

当我尝试从不同的线程访问主线程组件(PictureBox)时,我还出现了 System.InvalidOperationException:对象当前正在使用

MethodInvoker Delegate解决了这个问题。

Invoke((MethodInvoker)(delegate ()
{
   var image = PictureBox.Image;
}));