1

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

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

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

new Bitmap(myPicBox.Image); 

ETC...

我在某处读到计时器的间隔应该至少为 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 中的线程计时器调用导致了这个....

4

3 回答 3

1

You've got too many threads going to bring this to a good end. Both the Timer and the delegate's BeginInvoke() method will use a threadpool thread. The problem is that the PictureBox.Image property is only partially thread-safe. It can be accessed by only one thread at a time. Your code will die with an exception when the image is painted by the UI thread at the exact same time your code is calling the Clone() method.

Your lock statement doesn't solve the problem, the PictureBox is accessing the Image property without using that same lock. I would strongly recommend getting rid of the threading first, use a System.Windows.Forms.Timer instead of a System.Threading.Timer. It's Tick event is raised on the UI thread. That will however make the UI thread unresponsive while the event is running, it depends how long it takes whether that's noticeable to the user. More than, say, 100 milliseconds gets to be a problem.

The only other approach is to try to make the PictureBox control thread-safe. That's possible to some degree. Add a new class to your project and paste the code shown below. Compile. Drop the new control from the top of the toolbox onto your form, replacing the existing PB. Beware that this is only a partial solution, displaying an animated GIF or using the ImageLocation property will still bomb. Use the provided Clone method instead of calling Clone on the Image property.

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);
        }
    }
}
于 2010-09-13T11:21:19.570 回答
1

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

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

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

于 2010-09-13T06:15:45.167 回答
1

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

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

于 2010-09-13T06:21:55.390 回答