简体   繁体   English

如何在c#中将对安全调用PictureBox.Image的线程进行线程处理,当前给出3个错误之一

[英]How to thread safe call to PictureBox.Image in c# , currently gives one of 3 errors

I am using this PictureBox on a form, to this picture box I use the AForge Code. 我在窗体上使用此PictureBox,对此图片使用AForge代码。 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. 我将pictureBox的引用传递到我创建的网络摄像头类中,该类初始化了网络摄像头并告诉它将帧绘制到的位置....因此它很高兴地将其绘制到帧...没有问题。

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: 但是然后某些时候(当我想用所说的图像做东西时,如果单击一个chck框)...我使用简单的代码启动此计时器:

timer1.Enabled = true;

this timer's interval is set to 33. 此计时器的间隔设置为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"); 我在其他地方看到有人说这可能是线程问题,请放这行: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? 因此,我在该time1_click方法的顶部进行了操作,但是断言似乎没有发生,但是我不确定这是否是断言的正确位置... timer1_click是否在UI线程中?

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: 或在该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);
}

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? 是否出于某种原因创建了一个代表,该代表返回该double值,并且如果invoke required是真的会被调用,则创建一个委托吗?

is timer1_click in a UI thread or not? 是timer1_click是否在UI线程中?

Depends on which timer you are using. 取决于您使用的计时器。 sendDataFast definitely isn't because you used a System.Threading.Timer. sendDataFast绝对不是因为您使用了System.Threading.Timer。

If you take a look at the MSDN documentation on System.Threading.Timer you'll see the following 如果您查看有关System.Threading.Timer的MSDN文档,您将看到以下内容

System.Threading.Timer is a simple, lightweight timer that uses callback methods and is served by thread pool threads. System.Threading.Timer是一个简单,轻便的计时器,它使用回调方法,并由线程池线程提供服务。 It is not recommended for use with Windows Forms, because its callbacks do not occur on the user interface thread. 不建议将其与Windows窗体一起使用,因为其回调不会在用户界面线程上发生。 System.Windows.Forms.Timer is a better choice for use with Windows Forms.choice for use with Windows Forms. System.Windows.Forms.Timer是与Windows Forms.choice一起使用的更好选择。

This is explains why you're getting freezes. 这就是为什么您会冻结的原因。

The callback method executed by the timer should be reentrant, because it is called on ThreadPool threads. 计时器执行的回调方法应该是可重入的,因为它是在ThreadPool线程上调用的。 The callback can be executed simultaneously on two thread pool threads if the timer interval is less than the time required to execute the callback, or if all thread pool threads are in use and the callback is queued multiple times. 如果计时器间隔小于执行回调所需的时间,或者所有线程池线程都在使用中,并且回调多次排队,则可以在两个线程池线程上同时执行回调。

Which means if your function fails to execute in under 33 ms the function will be called again. 这意味着如果您的函数未能在33毫秒内执行,该函数将再次被调用。 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. 因此,我更喜欢System.Timers.Timer。 It has an AutoReset Property that set false. 它具有设置为false的AutoReset属性。 Then at the end of my function I call Timer.Start. 然后,在函数结束时,我调用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 乔恩·斯凯特(Jon Skeet)对开始调用问题的回答

Eric Lippert Blog on what an OutOfMemory Exception likely is 关于可能发生OutOfMemory异常的Eric Lippert博客

I think while seeing this line 我想看到这条线

double value = detector.ProcessFrame(new Bitmap(picCapture.Image)); 

you are trying to modify the picCapture.Image which is a Picturebox image and you are doing this every 33 millisecs. 您正在尝试修改picPicturebox图像picCapture.Image,并且每33毫秒进行一次此操作。

1st What this detector.ProcessFrame do? 1st这个detector.ProcessFrame做什么?

2- You should pass the actual image uri to the New Bitmap rather than using a Image which is the source of PictureBox 2-您应该将实际的图像uri传递给New Bitmap,而不要使用作为PictureBox的源的Image

3- Why are you creating more timers in tick event ???? 3-为什么在滴答事件中创建更多计时器?

For OutOfMemoryException, I would suggest replacing 对于OutOfMemoryException,我建议替换

double value = detector.ProcessFrame(new Bitmap(picCapture.Image));

with

double value;
using(Bitmap bmp = new Bitmap(picCapture.Image)) {
    value = detector.ProcessFrame(bmp);
}

so your temporary Bitmap will be disposed as it should be. 因此您的临时位图将按原样处置。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM