我在表单上使用此PictureBox,对于此图片框我使用AForge代码。我将pictureBox的REFERENCE传递到我创建的网络摄像头类中,初始化网络摄像头并告诉它将帧绘制到哪里....所以它很乐意绘制帧...没问题。
但是某些时候(当我想用所述图像做事时,如果点击一个chck框)......我用简单的代码启动这个计时器:
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);
}
上面的这一行有我在其上看到的三个错误之一(堆栈跟踪可用):
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)
我确信这些是线程问题,但我不知道如何处理它们......我完全迷失了。如果程序挂在上面的那一行,我通常可以在调试器中再次单击运行,一切都很顺利。但我不想马虎,只是把它继续下去。我想弄清楚这个问题的根源..
我在其他地方看到有人说这可能是一个线程问题并且放下这一行: System.Diagnostics.Debug.Assert(!this.InvokeRequired,“InvokeRequired”);
所以我在那个time1_click方法的顶部做了但是断言似乎没有发生,但我不确定这是断言的正确位置...是否在UI线程中是timer1_click? / p>
我现在怀疑我用我初始化网络摄像头类的方式检查了我的代码:
或者在那个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
发生的那三个错误不会立即发生,似乎随机发生三个......导致我认为它是一个线程问题,但我怎么能解决这个问题呢?由于某种原因创建一个委托,返回那个double值,如果invoke required为true则被调用?
答案 0 :(得分:2)
是否在UI线程中点击了timer1_click?
取决于您使用的计时器。 sendDataFast肯定不是因为你使用了System.Threading.Timer。
如果您查看System.Threading.Timer上的MSDN文档,您将看到以下内容
System.Threading.Timer很简单, 使用回调的轻量级计时器 方法,由线程池提供 线程。不建议使用 用Windows Forms,因为它 用户不会发生回调 接口线程。 System.Windows.Forms.Timer是一个更好的 选择与Windows Forms.choice一起使用以与Windows窗体一起使用。
这就解释了为什么你会冻结。
由。执行的回调方法 计时器应该是可重入的,因为它 在ThreadPool线程上调用。该 回调可以执行 同时在两个线程池上 如果定时器间隔较小,则为线程 比执行所需的时间 回调,或者如果所有线程池 线程正在使用中,回调是 排队多次。
这意味着如果您的功能在33 ms内无法执行,则将再次调用该函数。这可能是这种情况,为什么你得到了你所看到的例外。两个或多个线程正在尝试使用同一个文件。您可能还有多个线程尝试分配大块内存,而一个线程无法获取您请求的大小块。不确定为什么你得到参数异常,但可能是因为前两个。
出于这个原因,我更喜欢System.Timers.Timer。它有一个设置为false的AutoReset属性。然后在我的函数结束时,我调用Timer.Start。你可以和其他计时器完成同样的事情,但它有点小问题。
以下是您可能会发现有用的三个链接
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
答案 1 :(得分:1)
我想看到这一行
double value = detector.ProcessFrame(new Bitmap(picCapture.Image));
你正在尝试修改picCapture.Image,它是一个Picturebox图像,你每33毫秒就会这样做。
1st这个detector.ProcessFrame做什么?
2-您应该将实际图像uri传递给新位图而不是使用作为PictureBox源的图像
3-为什么要在tick事件中创建更多的计时器????
答案 2 :(得分:1)
对于OutOfMemoryException,我建议替换
double value = detector.ProcessFrame(new Bitmap(picCapture.Image));
与
double value;
using(Bitmap bmp = new Bitmap(picCapture.Image)) {
value = detector.ProcessFrame(bmp);
}
因此您的临时位图将按原样处理。