代码之家  ›  专栏  ›  技术社区  ›  Codejoy

InvokeRequired在PictureBox上为true。怎么处理?

  •  1
  • Codejoy  · 技术社区  · 14 年前

    我还有一个 question 在我的picturebox电话中,有三种错误,其中一些很好的答案来自 Conrad Frix 是的。所以它让我知道我的问题在哪里,但现在要解决它,我不是百分之百确定。

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

    我想我所有的计时器都在用我的画板创造一个糟糕的比赛环境,我在几个地方用它来获取图像:

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

    根据我之前的问题,在Timer1u tick事件中似乎不再出现内存问题或其他错误…(内存不足错误为1)。

    我认为最大的问题是当我需要piccapture.invokeRequired的图像数据时,如何处理它?我敢肯定是定时器内部的线程计时器调用导致了这个……

    3 回复  |  直到 8 年前
        1
  •  1
  •   Jon Skeet    14 年前

    顾名思义, InvokeRequired 意思是你需要打电话 Invoke (或 BeginInvoke )访问控件时。

    注意这是 Control.Invoke / Control.BeginInvoke , 这个 Invoke/BeginInvoke 出席代表…尽管你需要一个代表 呼叫 援引 / 异步调用 ,只是为了增加混乱。

    Windows Forms section of my threading tutorial 更多细节。整个教程可以做的更新,但我相信这一点是好的。在其他情况下,您也可以考虑使用 BackgroundWorker ,但我认为在这种情况下,这可能与你无关。

        2
  •  1
  •   Timwi    14 年前

    我认为你对InvokeRequired的理解有误。invokeRequired表示当前线程与UI线程不同,现在访问控件状态是不安全的。如果是这样的话你就得用 Control.Invoke 封送对ui线程的调用,然后访问控件状态。阅读 here on MSDN 更多信息。

    在您的情况下,除非PictureBox图像正在更改,否则我建议您最好提前复制图像并使用它。否则你需要使用 控件调用 是的。

        3
  •  1
  •   Hans Passant    14 年前

    你有太多的线索要结束这一切。计时器和委托的beginInvoke()方法都将使用线程池线程。问题是picturebox.image属性只是部分线程安全的。一次只能有一个线程访问它。当ui线程在调用clone()方法的同时绘制图像时,代码将异常终止。

    你的lock语句不能解决这个问题,picturebox正在访问image属性而不使用同一个锁。我强烈建议首先去掉线程,使用system.windows.forms.timer而不是system.threading.timer。它的tick事件是在ui线程上引发的。但是,这将使ui线程在事件运行时没有响应,这取决于用户是否注意到这一点需要多长时间。比如说,超过100毫秒是个问题。

    唯一的另一种方法是尝试使picturebox控件线程安全。这在某种程度上是可能的。向项目中添加一个新类并粘贴下面显示的代码。编译。将新控件从工具箱的顶部放到您的窗体上,替换现有的PB。请注意,这只是一个局部解决方案,显示动画gif或使用imagelocation属性仍然会爆炸。使用提供的克隆方法,而不是对映像属性调用克隆。

    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);
            }
        }
    }
    
    推荐文章