如何在 c# 中对 PictureBox.Image 进行线程安全调用,当前给出 3 个错误之一
我在表单上使用这个图片框,对于这个图片框我使用 AForge 代码。我将 pictureBox 的 REFERENCE 传递到我创建的网络摄像头类中,该类初始化网络摄像头并告诉它在哪里绘制框架......所以它很高兴地绘制它的框架......没问题。
但后来某些时候(当我想对所述图像执行操作时,如果单击了复选框)...我使用简单的代码启动此计时器:
timer1.Enabled = true;
此计时器的间隔设置为 33。
所以现在它每次都会触发我的代码循环如下:
private void timer1_Tick(object sender, EventArgs e)
{
...
double value = detector.ProcessFrame(new Bitmap(picCapture.Image)); //errors here
...
TimerCallback tc = new TimerCallback(sendDataFast);
System.Threading.Timer t = new System.Threading.Timer(tc, null, 2000, Timeout.Infinite);
}
上面的行有我在其上看到的三个错误之一(堆栈跟踪可用):
对象当前正在其他地方使用。
内存不足。 (“System.OutOfMemoryException”类型的第一次机会异常发生在 System.Drawing.dll 中)
参数无效 (System.Drawing.dll 中第一次出现“System.ArgumentException”类型的异常)
我确信这些是线程问题,但我不知道如何处理它们......我完全迷失了。如果程序挂在上面的那一行,我通常可以在调试器中再次单击“运行”,一切都会很好。但我不想马虎,只是随意地尝试捕捉并继续。我想找出这个问题的根源..
我在其他地方看到有人说这可能是线程问题并添加这一行: System.Diagnostics.Debug.Assert(!this.InvokeRequired, "InvokeRequired");
所以我在 time1_click 方法的顶部做了,但断言似乎没有发生,但我不确定这是断言的正确位置...timer1_click 是否在 UI 线程中?
我现在怀疑我用初始化网络摄像头类的方式检查了我的代码:
或者在那个timer1_click中我还调用了这个方法:
void sendDataFast(Object stateObject)
{
EmergencyDelegate delEmergency =
new EmergencyDelegate(mic.sendDataEmergency);
// call the BeginInvoke function! //sendDataEmergency takes in a picture Image picImage as an argument.
delEmergency.BeginInvoke(picCapture.Image, null, null);
}
为了完整起见,这就是我初始化网络摄像头类的方式:
webcam = new WebCam();
webcam.InitializeWebCam(ref picCapture, ref picComparator, ref dataObject, this); //guessing this is calling threading issues
发生的这三个错误不不会立即发生,似乎是三个中的一个随机发生......让我认为这是一个线程问题,但我还能如何解决这个问题?出于某种原因创建一个委托,返回该双精度值,并在 invoke required 为 true 时调用该委托?
I am using this PictureBox on a form, to this picture box I use the AForge Code. I pass the REFERENCE of the pictureBox into a webcam class I create that initializes the webcam and tells it where to draw its frames to....so it happily draws it frames... no problemo.
But then certain times (when I want to do stuff with said image, if a chck box is clicked)...I start this timer using simple code:
timer1.Enabled = true;
this timer's interval is set to 33.
So now its firing along and every time through the loop my code has this:
private void timer1_Tick(object sender, EventArgs e)
{
...
double value = detector.ProcessFrame(new Bitmap(picCapture.Image)); //errors here
...
TimerCallback tc = new TimerCallback(sendDataFast);
System.Threading.Timer t = new System.Threading.Timer(tc, null, 2000, Timeout.Infinite);
}
That line above has one of three errors I have seen on it (Stack traces where available):
Object is currently in use elsewhere.
Out of Memory.
(A first chance exception of type 'System.OutOfMemoryException' occurred in System.Drawing.dll)
Parameter not valid
(A first chance exception of type 'System.ArgumentException' occurred in System.Drawing.dll)
I am certain these are threading issues but I have no clue how to deal with them...I am totally lost. If the program hangs on that line above, I can usually click run again in the debugger and all is well. But I don't want to be sloppy and just put in a willy nilly try catch that continues. I would like to figure out the root of this issue..
I saw somewhere else someone said it could be a threading issue and to put this line:
System.Diagnostics.Debug.Assert(!this.InvokeRequired, "InvokeRequired");
So I did at the top of that time1_click method but the assert doesn't seem to be happening, but i am not sure this was the right place for the assert... is timer1_click in a UI thread or not?
I suspect now that I reviewed my code its something with the way I initialize my webcam class:
Or within that timer1_click I also make a call to this method:
void sendDataFast(Object stateObject)
{
EmergencyDelegate delEmergency =
new EmergencyDelegate(mic.sendDataEmergency);
// call the BeginInvoke function! //sendDataEmergency takes in a picture Image picImage as an argument.
delEmergency.BeginInvoke(picCapture.Image, null, null);
}
And for completeness this is how I initialize my webcam class:
webcam = new WebCam();
webcam.InitializeWebCam(ref picCapture, ref picComparator, ref dataObject, this); //guessing this is calling threading issues
Those three errors that happen don't happen right away, seems to happen randomly one of the three.... leads me to think its a threading issue but how else can I fix this? creating a delegate for some reason that returns that double value and is called if invoke required is true?
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(3)
取决于您使用的计时器。 sendDataFast 绝对不是因为您使用了 System.Threading.Timer。
如果您查看有关 System.Threading.Timer 的 MSDN 文档,您将看到以下内容
这可以解释为什么你会被冻结。
这意味着如果您的函数在 33 毫秒内未能执行,该函数将被再次调用。这可能就是这种情况,也是您遇到异常的原因。两个或多个线程正在尝试使用同一个文件。您还可能有多个线程尝试分配一大块内存,但其中一个线程无法获取您所请求大小的块。不确定为什么您会收到参数异常,但这可能是因为前两个。
出于这个原因,我更喜欢 System.Timers.Timer。它有一个设置为 false 的 AutoReset 属性。然后在函数结束时我调用 Timer.Start。你可以用其他计时器完成同样的事情,但它有点诡计。
以下是您可能会发现有用的三个链接
有关计时器类比较的文章
Jon Skeet 回答开始调用问题
Eric Lippert 博客介绍了可能出现的 OutOfMemory 异常< /a>
Depends on which timer you are using. sendDataFast definitely isn't because you used a System.Threading.Timer.
If you take a look at the MSDN documentation on System.Threading.Timer you'll see the following
This is explains why you're getting freezes.
Which means if your function fails to execute in under 33 ms the function will be called again. This is probably the case and why you're getting the exceptions you're seeing. Two or more threads are trying to use the same file. You may also have multiple threads trying to allocate a large block of Memory and one fails to get the block of the size you've requested. Not sure why you're getting the argument exception but it may be because of the previous two.
For this reason I prefer the System.Timers.Timer. It has an AutoReset Property that set false. Then at the end of my function I call Timer.Start. You can accomplish the same thing with the other timers but its a little tricker.
Here are three links you might find useful
Article on Comparison of the Timer Classes
Jon Skeet Answer on a Begin Invoke Question
Eric Lippert Blog on what an OutOfMemory Exception likely is
我认为当看到这一行时,
您正在尝试修改 picCapture.Image(这是一个 Picturebox 图像),并且您每 33 毫秒执行一次此操作。
第一这个 detector.ProcessFrame 做什么?
2- 您应该将实际图像 uri 传递给新位图,而不是使用作为 PictureBox 源的图像
3- 为什么要在刻度事件中创建更多计时器???
I think while seeing this line
you are trying to modify the picCapture.Image which is a Picturebox image and you are doing this every 33 millisecs.
1st What this detector.ProcessFrame do?
2- You should pass the actual image uri to the New Bitmap rather than using a Image which is the source of PictureBox
3- Why are you creating more timers in tick event ????
对于 OutOfMemoryException,我建议替换
为
,以便您的临时位图将按应有的方式处置。
For OutOfMemoryException, I would suggest replacing
with
so your temporary Bitmap will be disposed as it should be.