【问题标题】: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 交互。