2009-12-05 194 views
20

我已经通过this SO question但它没有帮助。InvalidOperationException - 对象当前正在其他地方使用

这里的情况是不同的。我正在使用Backgroundworkers。第一BackgroundWorker的开始上的用户图像输入和内部firstbackgroundworker_runworkercompleted()我使用调用其他3个backgroundworkers

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())中得到这个;

我不知道发生了什么。

回答

39

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

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

19

因此,它看起来像您的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以避免这种情况。

1

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

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

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

相关问题