C# InvalidOperationException - 对象当前正在别处使用 - 红十字
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/1060280/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
InvalidOperationException - object is currently in use elsewhere - red cross
提问by
I have a C# desktop application in which one thread that I create continously gets an image from a source(it's a digital camera actually) and puts it on a panel(panel.Image = img) in the GUI(which must be another thread as it is the code-behind of a control.
我有一个 C# 桌面应用程序,其中我创建的一个线程从源(实际上是数码相机)获取图像并将其放在 GUI 中的面板(panel.Image = img)上(必须是另一个线程作为它是控件的代码隐藏。
The application works but on some machines I get the following error at random time intervals(unpredictable)
该应用程序有效,但在某些机器上,我在随机时间间隔(不可预测)收到以下错误
************** Exception Text **************
System.InvalidOperationException: The object is currently in use elsewhere.
Then the panel turns into a red cross, red X - i think this is the invalid picture icon that is editable from the properties. The application keeps working but the panel is never updated.
然后面板变成一个红十字,红色 X - 我认为这是可从属性编辑的无效图片图标。应用程序继续工作,但面板永远不会更新。
From what I can tell this error comes from the control's onpaint event where I draw something else on the picture.
据我所知,此错误来自控件的 onpaint 事件,在该事件中我在图片上绘制了其他内容。
I tried using a lock there but no luck :(
我尝试在那里使用锁,但没有运气:(
The way I call the function that puts the image on the panel is as follows:
我调用将图像放在面板上的函数的方式如下:
if (this.ReceivedFrame != null)
{
Delegate[] clients = this.ReceivedFrame.GetInvocationList();
foreach (Delegate del in clients)
{
try
{
del.DynamicInvoke(new object[] { this,
new StreamEventArgs(frame)} );
}
catch { }
}
}
this is the delegate:
这是代表:
public delegate void ReceivedFrameEventHandler(object sender, StreamEventArgs e);
public event ReceivedFrameEventHandler ReceivedFrame;
and this is how the function inside the control code-behind registers to it:
这就是控制代码隐藏内部的函数向它注册的方式:
Camera.ReceivedFrame +=
new Camera.ReceivedFrameEventHandler(camera_ReceivedFrame);
I also tried
我也试过
del.Method.Invoke(del.Target, new object[] { this, new StreamEventArgs(b) });
instead of
代替
del.DynamicInvoke(new object[] { this, new StreamEventArgs(frame) });
but no luck
但没有运气
Does anyone know how I could fix this error or at least catch the error somehow and make the thread put the images on the panel once again?
有谁知道我如何解决这个错误,或者至少以某种方式捕获错误并使线程再次将图像放在面板上?
回答by Ahmed Said
I think this is multithreading problem Use windows golden rule and update the panel in the main thread use panel.Invoke This should overcome cross threading exception
我认为这是多线程问题 使用 windows 黄金法则并在主线程中更新面板使用 panel.Invoke 这应该可以克服跨线程异常
回答by arbiter
This is because Gdi+ Image class is not thread safe. Hovewer you can avoid InvalidOperationException by using lock every time when you need to Image access, for example for painting or getting image size:
这是因为 Gdi+ Image 类不是线程安全的。但是,您可以在每次需要图像访问时使用锁来避免 InvalidOperationException,例如用于绘制或获取图像大小:
Image DummyImage;
// Paint
lock (DummyImage)
e.Graphics.DrawImage(DummyImage, 10, 10);
// Access Image properties
Size ImageSize;
lock (DummyImage)
ImageSize = DummyImage.Size;
BTW, invocation is not needed, if you will use the above pattern.
顺便说一句,如果您将使用上述模式,则不需要调用。
回答by Dennis Kuhn
Seems to me, that the same Camera object is used several times.
在我看来,同一个 Camera 对象被多次使用。
E.g. try to use a new buffer for each received frame. It seems to me, that while the picture box is drawing the new frame, your capture library fills that buffer again. Therefore on faster machines this might not be an issue, with slower machines it might be an issue.
例如,尝试为每个接收到的帧使用一个新的缓冲区。在我看来,当图片框绘制新帧时,您的捕获库再次填充该缓冲区。因此,在较快的机器上,这可能不是问题,而在较慢的机器上,这可能是一个问题。
I've programmed something similar once, after each received frame, we had to request to receive the next frame and set the NEWframe receive buffer in that request.
我曾经编写过类似的程序,在每个接收到的帧之后,我们必须请求接收下一帧并在该请求中设置新的帧接收缓冲区。
If you can not do that, copy the received frame from the camera first to a new buffer and append that buffer to a queue, or just use 2 alternating buffers and check for overruns. Either use myOutPutPanel.BeginInvoke to call the camera_ReceivedFrame method, or better have a thread running, which checks the queue, when it has a new entry it calls mnyOutPutPanel.BeginInvoke to invoke your method to set the new buffer as image on the panel.
如果您不能这样做,请先将接收到的帧从相机复制到新缓冲区并将该缓冲区附加到队列中,或者仅使用 2 个交替缓冲区并检查是否溢出。要么使用 myOutPutPanel.BeginInvoke 调用 camera_ReceivedFrame 方法,要么最好运行一个线程来检查队列,当它有新条目时,它调用 mnyOutPutPanel.BeginInvoke 来调用您的方法将新缓冲区设置为面板上的图像。
Furthermore, once you received the buffer, use the Panel Invoke Method to invoke the setting of the image (guarantee that it runs in the window thread and not the thread from your capture library).
此外,一旦您收到缓冲区,请使用面板调用方法来调用图像的设置(保证它在窗口线程中运行,而不是在捕获库中的线程中运行)。
The example below can be called from any thread (capture library or other separate thread):
下面的示例可以从任何线程(捕获库或其他单独的线程)调用:
void camera_ReceivedFrame(object sender, StreamEventArgs e)
{
if(myOutputPanel.InvokeRequired)
{
myOutPutPanel.BeginInvoke(
new Camera.ReceivedFrameEventHandler(camera_ReceivedFrame),
sender,
e);
}
else
{
myOutPutPanel.Image = e.Image;
}
}
回答by Nick Gotch
I had a similar problem with the same error message but try as I might, locking the bitmap didn't fix anything for me. Then I realized I was drawing a shape using a static brush. Sure enough, it was the brush that was causing the thread contention.
我有一个类似的问题,同样的错误消息,但尽我所能,锁定位图并没有为我解决任何问题。然后我意识到我正在使用静态画笔绘制形状。果然,是刷子引起了线程争用。
var location = new Rectangle(100, 100, 500, 500);
var brush = MyClass.RED_BRUSH;
lock(brush)
e.Graphics.FillRectangle(brush, location);
This worked for my case and lesson learned: Check all the reference types being used at the point where thread contention is occurring.
这适用于我的案例和经验教训:检查发生线程争用时正在使用的所有引用类型。