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

发布于 2024-09-18 13:40:13 字数 1972 浏览 3 评论 0原文

我在表单上使用这个图片框,对于这个图片框我使用 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 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(3

沐歌 2024-09-25 13:40:14

timer1_click是否在UI线程中?

取决于您使用的计时器。 sendDataFast 绝对不是因为您使用了 System.Threading.Timer。

如果您查看有关 System.Threading.Timer 的 MSDN 文档,您将看到以下内容

System.Threading.Timer 是一个简单的、
使用回调的轻量级计时器
方法并由线程池提供服务
线程。不建议使用
使用 Windows 窗体,因为它
用户不会发生回调
接口线程。
System.Windows.Forms.Timer 是一个更好的
选择与 Windows 窗体一起使用。选择与 Windows 窗体一起使用。

这可以解释为什么你会被冻结。

执行的回调方法
定时器应该是可重入的,因为它
在 ThreadPool 线程上调用。这
回调可以执行
同时在两个线程池上
如果计时器间隔较小,则线程
比执行所需的时间
回调,或者如果所有线程池
线程正在使用中,回调是
排队多次。

这意味着如果您的函数在 33 毫秒内未能执行,该函数将被再次调用。这可能就是这种情况,也是您遇到异常的原因。两个或多个线程正在尝试使用同一个文件。您还可能有多个线程尝试分配一大块内存,但其中一个线程无法获取您所请求大小的块。不确定为什么您会收到参数异常,但这可能是因为前两个。

出于这个原因,我更喜欢 System.Timers.Timer。它有一个设置为 false 的 AutoReset 属性。然后在函数结束时我调用 Timer.Start。你可以用其他计时器完成同样的事情,但它有点诡计。

以下是您可能会发现有用的三个链接

有关计时器类比较的文章

Jon Skeet 回答开始调用问题

Eric Lippert 博客介绍了可能出现的 OutOfMemory 异常< /a>

is timer1_click in a UI thread or not?

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

System.Threading.Timer is a simple,
lightweight timer that uses callback
methods and is served by thread pool
threads. It is not recommended for use
with Windows Forms, because its
callbacks do not occur on the user
interface thread.
System.Windows.Forms.Timer is a better
choice for use with Windows Forms.choice for use with Windows Forms.

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. 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. 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

任谁 2024-09-25 13:40:14

我认为当看到这一行时,

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

您正在尝试修改 picCapture.Image(这是一个 Picturebox 图像),并且您每 33 毫秒执行一次此操作。

第一这个 detector.ProcessFrame 做什么?

2- 您应该将实际图像 uri 传递给新位图,而不是使用作为 PictureBox 源的图像

3- 为什么要在刻度事件中创建更多计时器???

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.

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 ????

眼泪都笑了 2024-09-25 13:40:14

对于 OutOfMemoryException,我建议替换

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

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

,以便您的临时位图将按应有的方式处置。

For OutOfMemoryException, I would suggest replacing

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.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文