我经历过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())中获取;
我不知道发生了什么事。
答案 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;
}));