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

39

我已经阅读了这个SO问题,但它没有帮助。

这里的情况有所不同。我正在使用 Backgroundworkers。第一个 Backgroundworker 开始在用户的图像输入上运行,并在 firstbackgroundworker_runworkercompleted() 中调用另外 3 个 Backgroundworkers。

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

这是 each 的代码:

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
}

在其他算法的backgroundworker_doWork()中执行类似的操作。

现在我有时会收到“InvalidOperationException - object is currently in use elsewhere”的错误。这是非常武断的。有时候我会在algo1backgroundworker_DoWork和algo2backgroundworker_DoWork以及Application.Run(new myWindowsForm())中遇到这个问题。

我对发生的情况一无所知。

3个回答

70

GDI+内部有一个锁,防止两个线程同时访问位图。这不是一种阻塞式的锁,而是一种“程序员犯了错误,我会抛出异常”的锁。你的线程崩溃是因为你在所有线程中克隆了图像(即访问位图)。你的UI线程会崩溃,因为它正在尝试在一个线程克隆图像时绘制位图(即访问位图)。

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


31

看起来你的后台工作线程正在尝试在同一时间访问相同的 Windows Forms 组件。这解释了为什么失败是随机的。

你需要通过使用一个 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实例被所有方法使用。当后台工作者方法进入其lock{}部分时,到达该点的其他方法将被阻塞。因此,重要的是要确保锁定部分中的代码速度快。
当您来“输出”经过处理的图像时,也要小心,确保不要对UI进行跨线程更新。查看此帖子了解避免这种情况的巧妙方法。

5
在Windows Forms中,你不仅应该只从一个线程访问控件,而且该线程应该是主应用程序线程,即创建控件的线程。
这意味着在DoWork中,你不应该访问任何控件(除非使用Control.Invoke)。因此,在这里,你应该调用RunWorkerAsync并传入你的图像克隆。在DoWork事件处理程序中,你可以从DoWorkEventArgs.Argument中提取参数。
只有ProgressChanged和RunWorkerCompleted事件处理程序应该与GUI交互。

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