C# 关闭多线程应用程序时已释放ObjectDisposed

C# 关闭多线程应用程序时已释放ObjectDisposed,c#,multithreading,invoke,aforge,objectdisposedexception,C#,Multithreading,Invoke,Aforge,Objectdisposedexception,可能重复: **关于可能的重复-BackgroundWorker方法在此不适用 下面是我尝试使用一个RGE库来接收来自IP摄像机的视频 每个视频流都应该在单独的线程中运行,在新帧到达时通知UI线程。事件处理程序在引发它的同一线程中执行,因此我需要使用Invoke 在我希望停止应用程序之前,所有操作都会顺利运行。标有“>>>”的行引发ObjectDisposed异常,因此我的应用程序不会像运行时那样顺利结束 我知道问题在于理解多线程,只是因为它而看不到真正的问题。有人能解释一下这里发生了什么吗

可能重复:

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

下面是我尝试使用一个RGE库来接收来自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.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();
  }
}

我认为问题在于:库在关闭表单后调用回调(
generic\u 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;
  }
  ...
}
另一种方法是等待而不是关闭表单,直到库仍在工作,然后在
FormClosing
FormClosing
事件处理程序中等待:

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();
  }
}

我认为问题在于:库在关闭表单后调用回调(
generic\u 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;
  }
  ...
}
另一种方法是等待而不是关闭表单,直到库仍在工作,然后在
FormClosing
FormClosing
事件处理程序中等待:

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();
  }
}

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

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

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

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

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

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

使用thread.Join()可防止表单关闭完成。不知何故,工作线程上的NewFrame永远不会结束,不管我是否检查pictureBox1.IsDisposed。好的,似乎用BeginInvoke替换Invoke解决了这个问题。老实说,我想了解为什么~~调用是一个同步调用。这意味着您将等待底层主体执行。我强烈建议找出问题的根源,而不是使用BeginInvoke。我假设对thread.Join()的调用会导致死锁,这就是表单不关闭的原因。如果(this.IsDisposed)返回;-值得怀疑,因为泛型_NewFrame没有在UI线程中执行。也许,如果我在UI线程上调用整个泛型_NewFrame,它会起作用。这似乎很合理,因为工作线程不应该过载,除了它们的直接用途之外。我试试看,谢谢。但是,如果在同一个线程上执行NewFrame和FormClosing,则无法确定竞态条件将如何改变。使用thread.Join()可防止FormClosing完成。不知何故,工作线程上的NewFrame永远不会结束,不管我是否检查pictureBox1.IsDisposed。好的,似乎用BeginInvoke替换Invoke解决了这个问题。老实说,我想了解为什么~~调用是一个同步调用。这意味着您将等待底层主体执行。我强烈建议找出问题的根源,而不是使用BeginInvoke。我假设对thread.Join()的调用会导致死锁,这就是表单不关闭的原因。如果(this.IsDisposed)返回;-值得怀疑,因为泛型_NewFrame没有在UI线程中执行。也许,如果我在UI线程上调用整个泛型_NewFrame,它会起作用。这似乎很合理,因为工作线程不应该过载,除了它们的直接用途之外。我试试看,谢谢。但是,如果在同一线程上执行NewFrame和FormClosing,则不确定竞争条件将如何改变。