【问题标题】:ObjectDisposed when closing multithreaded app [duplicate]关闭多线程应用程序时的 ObjectDisposed [重复]
【发布时间】:2012-12-18 12:34:30
【问题描述】:

可能重复:
How to stop BackgroundWorker on Form’s Closing event?

**关于可能的重复 - BackgroundWorker 方法在这里不适用。

以下是我尝试使用 AForge 库从 IP 摄像机接收视频。

每个视频流都应该在单独的线程中运行,当新帧到达时通知 UI 线程。事件处理程序在引发它的同一线程中执行,因此我需要使用 Invoke。

一切顺利,直到我想停止应用程序。标有 '>>>' 的行会引发 ObjectDisposed 异常,因此我的应用程序不会像运行时那样顺利结束。

我知道问题在于理解多线程,因此看不到真正的问题。有人可以解释一下这里发生了什么吗?

Form1.cs

public void generic_NewFrame(object sender, NewFrameEventArgs e)
{
  ...
  if (pictureBox1.InvokeRequired)
  {                            
>>>   pictureBox1.Invoke(new MethodInvoker(delegate()
                         {                                                       
                           pictureBox1.BackgroundImage = (Image)buf;
                         }));
  }
  else
  {
    pictureBox1.BackgroundImage = (Image)buf;
  }
  ...
}

尽可能短,Camera 类:

Camera.cs
//Camera thread loop
private void WorkerThread()
{
  while (!stopEvent.WaitOne(0, false))
  {
   ...
     if (!stopEvent.WaitOne(0, false))
     {
       // notify UI thread
       OnNewFrame(new NewFrameEventArgs(Last_frame));
   ...
  }  
}

override public void Play()
{
  stopEvent = new ManualResetEvent(false);

  thread = new Thread(new ThreadStart(WorkerThread));
  thread.Start();
}

override public void Stop()
{
  if (thread != null)
  {
    stopEvent.Set();
  }
}

【问题讨论】:

    标签: c# multithreading invoke aforge objectdisposedexception


    【解决方案1】:

    我认为问题在于:库在关闭表单之后调用您的回调 (generic_NewFrame)。 你可以用几种不同的方法来修复它。

    首先,如果你的表单已经被释放,你可以跳过你的回调方法:

    public void generic_NewFrame(object sender, NewFrameEventArgs e)
    {
      // Lets skip this callback if our form already closed
      **if (this.IsDisposed) return;**
    
      ...
      if (pictureBox1.InvokeRequired)
      {                            
    >>>   pictureBox1.Invoke(new MethodInvoker(delegate()
                             {                                                       
                               pictureBox1.BackgroundImage = (Image)buf;
                             }));
      }
      else
      {
        pictureBox1.BackgroundImage = (Image)buf;
      }
      ...
    }
    

    另一种方法是等待而不是关闭表单,直到您的库仍在工作并在 FormClosingFormClosed 事件处理程序中等待:

    private void FormClosingEventHandler(object sender, CancelEventArgs e)
    {
      // Waiting till your worker thread finishes
       _thread.Join();
    }
    

    或者你可以在你的停止方法中等待:

    override public void Stop()
    {
      if (thread != null)
      {
        stopEvent.Set();
        thread.Join();
      }
    }
    

    【讨论】:

    • 使用 thread.Join() 防止 FormClosing 完成。不知何故,工作线程上的 NewFrame 永远不会结束,无论我是否检查 pictureBox1.IsDisposed 与否。
    • 好的,似乎用 BeginInvoke 替换 Invoke 解决了这个问题。老实说,想知道为什么~_~
    • Invoke 是一个同步调用。这意味着您将等到底层主体执行。我强烈建议找出问题的根源,而不是使用 BeginInvoke。我假设对 thread.Join() 的调用会导致死锁,这就是表单不关闭的原因。那么对 IsDisposed 进行额外检查呢?
    • if (this.IsDisposed) return; - 值得怀疑,因为 generic_NewFrame 不在 UI 线程中执行。也许,如果我在 UI 线程上调用整个 generic_NewFrame,它会起作用。这似乎很合理,因为工作线程除了其直接目的之外不应该被任何东西超载。我会试试的,谢谢。不过,不确定如果 NewFrame 和 FormClosing 在同一个线程上执行,竞争条件将如何变化。
    【解决方案2】:

    为避免导致这种情况的竞争条件,您可以执行以下操作:

    pictureBox1.Invoke(new MethodInvoker(delegate()
                         {                             
                           if (!pictureBox1.IsDisposed) 
                           {                      
                               pictureBox1.BackgroundImage = (Image)buf;
                           }
                         }));
    

    在 UI 线程上检查 IsDisposed 很重要,即在被调用的委托内。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2010-10-11
      • 2015-06-25
      • 2011-03-17
      • 2017-01-06
      • 1970-01-01
      相关资源
      最近更新 更多