如何从不同的线程访问对象而不会发生冲突?
Posted
技术标签:
【中文标题】如何从不同的线程访问对象而不会发生冲突?【英文标题】:How to access an object from a different thread without conflict? 【发布时间】:2011-11-04 03:49:40 【问题描述】:我有一个名为 fetchImage 的线程,它运行 startFetch() 方法,即
for (int i = 0; !stopFetching && i < 2000; i++)
control.fetchImage().Save("Fetch_Image\\fetchedFile" + fileName + ".JPG", System.Drawing.Imaging.ImageFormat.Jpeg);
fileName++;
//Thread.Sleep(800);
Console.WriteLine("Filename :" + fileName);
control 是另一个具有 fetchImage() 方法的类:
return (System.Drawing.Bitmap)mainForm.imageTemp.Clone();
mainForm
是一个具有名为 imageTemp 的 Image 变量的表单。现在startFetch()
在我拥有Thread.Sleep(800)
时运行良好,但它在没有睡眠的情况下给了我这个错误。这是错误的堆栈跟踪
System.InvalidOperationException was unhandled
Message=Object is currently in use elsewhere.
Source=System.Drawing
StackTrace:
at System.Drawing.Image.Clone()
at KiPNi.Control.fetchImage() in F:\CMPS285\285-fall10-5\Final working code\WindowsFormsApplication7\Control.cs:line 65
at KiPNi.Form4.startFetch() in F:\CMPS285\285-fall10-5\Final working code\WindowsFormsApplication7\Form4.cs:line 80
at System.Threading.ThreadHelper.ThreadStart_Context(Object state)
at System.Threading.ExecutionContext.Run(ExecutionContext executionContext, ContextCallback callback, Object state, Boolean ignoreSyncCtx)
at System.Threading.ExecutionContext.Run(ExecutionContext executionContext, ContextCallback callback, Object state)
at System.Threading.ThreadHelper.ThreadStart()
InnerException:
我对线程了解不多(虽然我在遇到这个问题后尝试过)。
如何从 fetchImage 线程访问图像?
【问题讨论】:
你真的需要发布fetchImage
正在做什么的代码。资源(又名文件)被另一个线程锁定。
一个位图一次只能被一个线程使用。我们可以看到您的工作线程正在访问它,这就是被炸毁的代码。它也被用于另一个线程。这可能是主线程,试图绘制位图。我猜你把它放在一个图片框里。除了不在 PB 中显示它或创建自己的控件以便您可以在 Graphics.DrawImage() 调用周围使用 lock 关键字之外,没有真正的解决方法。
@SilverNinja,抱歉我没有正确指定:fetchImage() 只是从 mainForm 返回图像imageTemp
的克隆。 return (System.Drawing.Bitmap)mainForm.imageTemp.Clone();
@HansPassant 我确实理解您所说的概念。我认为它也适用于其他 GUI 组件(调用线程不能更改另一个线程实例的值)。我没有使用Graphics.DrawImage()
but System.Drawing.Image 对象(Visual C#)。
我无法得到的是,如果我这样做Thread.sleep()
,它就可以正常工作。但是,如果我这样做,我每秒发送到服务器的图像将比我在没有间断的情况下循环它时更少。你是对的 imageTemp, imageTemp = pictureBox1.Image;
其中pictureBox1 是指网络摄像头视频。 @Hans Passant
【参考方案1】:
不是从处理线程拉数据,而是查看是否可以在帧更新时从 UI 线程推送数据 - 这样,您可以 可能避免锁定;如果您可以创建图像数据的克隆,则可以避免在线程之间共享对象。
无论如何,您实际上只应该从主线程调用 UI 控件上的方法(Control.BeginInvoke() 存在,但最好尝试修改您的设计以避免它,如果可能的话)。
【讨论】:
我明白你在说什么。我创建 imageTemp 的 Clone() 的原因是为了避免这种冲突return (System.Drawing.Bitmap)mainForm.imageTemp.Clone();
。现在我已经实现了一个Forms.Timer()
,它每 1/2 秒打勾。到目前为止,没有错误。但我真的很想每秒获取更多的帧(如果可能的话是连续的)。我真的很感激一个代码来证明你在说什么。感谢您的帮助
问题是您在另一个线程中调用 Clone 方法 - 这可能与在主 UI 线程中更新的控件发生冲突。尝试让 UI 线程执行克隆,并将其传递给工作线程 - 推送而不是拉取处理。
我明白了。感谢你的帮助。谢谢以上是关于如何从不同的线程访问对象而不会发生冲突?的主要内容,如果未能解决你的问题,请参考以下文章
单片机的程序存储器和数据存储器共处同一地址空间为啥不会发生总线冲突?