在 PictureBox 上 InvokeRequired 为真。如何处理?

Posted

技术标签:

【中文标题】在 PictureBox 上 InvokeRequired 为真。如何处理?【英文标题】:InvokeRequired is true on PictureBox. How to deal with this? 【发布时间】:2010-09-13 06:00:17 【问题描述】:

我的 PictureBox 电话中有另一个 question 给我 3 种错误,特别是来自 Conrad Frix 的一些很好的答案。所以它让我弄清楚我的问题出在哪里,但现在要解决它我不是 100% 确定的。

基本上我有一个 Windows 窗体计时器,它正在检查某个事件是否为真,如果是,它会告诉系统在所述事件(值)超过某个阈值后 2 秒发送一些数据。

我认为我拥有的所有计时器都在用我的 PictureBox 创建一个令人讨厌的竞争条件,我在几个地方使用它来获取图像:

new Bitmap(myPicBox.Image); 

等等……

我在某处读到,计时器的间隔应该至少为 50。将其设置为 33。我发现我可以执行 picCapture.InvokeRequired 来查看它是否会基本上死掉。我知道我需要使用一个委托,但只使用它们来设置一些东西......而不是从中获取图像......不知道如何设置它......我知道确实是什么原因造成的,就是这个代码组合:

private void timer1_Tick(object sender, EventArgs e)
    
          if(someCOnditionTrue)
          

                    TimerCallback tc = new TimerCallback(sendDataFast); //only 
                       //doing all this so i can have the method run two seconds after     
                       // the condition is detected to be true.
                    System.Threading.Timer t = new System.Threading.Timer(tc, null, 2000, Timeout.Infinite);
          
   



    void sendDataFast(Object stateObject)
    

        //using this so the execution is not haulted while the sending of data takes place.
        EmergencyDelegate delEmergency =
                     new EmergencyDelegate(mic.sendEmergencyData);

        Image imgclone;

        if (picCapture.InvokeRequired)
                         
            Console.WriteLine("HFS Batman! its going to die ");
        
        lock (lockObject2) //i admit no clue what im doing here and doesn't seem to help.
        
            Image img = picCapture.Image;
            imgclone = (Image)img.Clone();
        
        delEmergency.BeginInvoke(imgclone, null, null); //deep in the call to
        //sendEmergencyData i get the **ParameterNotValid** almost everytime.

        imgclone.Dispose(); //to free memory?


    

根据我之前的问题,在 timer1_tick 事件中似乎不再出现内存问题或其他错误...(内存不足错误是一个)。

我认为最大的问题是当我需要它的图像数据时如何处理 picCapture.InvokeRequired?我确定它在 timer1_click 中的线程定时器调用导致了这个....

【问题讨论】:

【参考方案1】:

顾名思义,InvokeRequired 表示访问控件时需要调用Invoke(或BeginInvoke)。

请注意,这是Control.Invoke/Control.BeginInvoke不是代表中存在的Invoke/BeginInvoke...尽管您需要一个代表才能调用 Invoke/BeginInvoke,只是为了增加混乱。

有关详细信息,请参阅Windows Forms section of my threading tutorial。整个教程可以做更新,但我相信这一点没问题。在其他情况下,您可能还想考虑使用BackgroundWorker,但我认为在这种特殊情况下这可能与您无关。

【讨论】:

谢谢我马上检查一下。我之前使用过委托来调用一个方法,但那是我的“真正的工作”,我这里没有代码,所以不确定我在那里做了什么......有一段时间了。 @Jon:关于yoda.arachsys.com/csharp/threads/shutdown.shtml 的例子,真的有必要在“停止”变量上使用锁吗?似乎它只能通过 Stop() 调用设置,并且永远不能取消设置。只是问,因为我用写了一个非常相似的类,但我没有锁定我等价的停止变量。 @SomethingBetter:您要么需要它是易失的,要么需要锁定。否则不能保证一个线程中的集合会在另一个线程中看到。【参考方案2】:

我认为您对 InvokeRequired 的理解有误。 InvokeRequired 表示当前线程与UI线程不同,现在访问控件状态是不安全的。如果是这种情况,那么您必须使用Control.Invoke 来编组对 UI 线程的调用,然后访问控制状态。阅读here on MSDN了解更多信息。

在您的情况下,除非 PictureBox 图像发生变化,否则我建议您先复制图像并使用它。否则需要使用Control.Invoke

【讨论】:

【参考方案3】:

你有太多的线程来结束这件事。 Timer 和委托的 BeginInvoke() 方法都将使用线程池线程。问题是 PictureBox.Image 属性只是部分线程安全的。它一次只能被一个线程访问。当 UI 线程在您的代码调用 Clone() 方法的同时绘制图像时,您的代码将异常终止。

您的 lock 语句没有解决问题,PictureBox 正在访问 Image 属性而不使用相同的锁。我强烈建议首先摆脱线程,使用 System.Windows.Forms.Timer 而不是 System.Threading.Timer。它的 Tick 事件在 UI 线程上引发。然而,这将使 UI 线程在事件运行时无响应,这取决于用户是否注意到这需要多长时间。例如,超过 100 毫秒就会成为问题。

唯一的其他方法是尝试使 PictureBox 控件线程安全。这在某种程度上是可能的。向您的项目添加一个新类并粘贴如下所示的代码。编译。将新控件从工具箱顶部拖放到表单上,替换现有的 PB。请注意,这只是部分解决方案,显示动画 GIF 或使用 ImageLocation 属性仍然会爆炸。使用提供的 Clone 方法,而不是在 Image 属性上调用 Clone。

using System;
using System.Drawing;
using System.Windows.Forms;

class MyPictureBox : PictureBox 
    private object locker = new object();
    public new Image Image 
        get  return base.Image; 
        set  lock (locker)  base.Image = value;  
    
    public Image Clone() 
        lock (locker) 
            return (this.Image != null) ? (Image)this.Image.Clone() : null;
        
    
    protected override void OnPaint(PaintEventArgs pe) 
        lock (locker) 
            base.OnPaint(pe);
        
    

【讨论】:

好的,我喜欢它的外观,让我感觉更加温暖和模糊。我看到线程太多的问题,我无法解决这个问题。我正在使用的计时器(至少是主要的)是一个窗口形式的计时器。其他的是线程计时器和委托。我正在尝试做的事情听起来很容易,但显然幕后发生的事情比想象的要多得多。我想我可以在我的表单上使用这个新控件,并且仍然可以通过该 Image 属性访问图像? 是的,这就是目的。试试看。

以上是关于在 PictureBox 上 InvokeRequired 为真。如何处理?的主要内容,如果未能解决你的问题,请参考以下文章

在 PictureBox 上 InvokeRequired 为真。如何处理?

如何在 Picturebox 上获得滚动条

在 XP 上运行时 Winform PictureBox 不透明

C# 在带有 Graphics.DrawLine() 的 PictureBox 上绘制不适用于 Paint 事件

如何检索 WinForms PictureBox 的缩放因子?

picturebox增加滚动条