1

Possible Duplicate:
How to stop BackgroundWorker on Form’s Closing event?

**Regarding possible duplicate - BackgroundWorker methods are not applicable here.

Below is my attempt to use AForge library to receive video from IP cameras.

Each video stream is supposed to run in separate thread, notifying UI thread when new frame arrives. Event handler is executed in the same thread, that raised it, so I need to use Invoke.

All runs smoothly until I wish to stop the application. The line marked with '>>>' throws ObjectDisposed exception, so my application doesn't end as smoothly as it runs.

I know problem is with understanding multithreading, just can't see the real problem because of it. Could someone please explain what happens here?

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;
  }
  ...
}

As short as possible, Camera class:

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();
  }
}
Community
  • 1
  • 1
ZuOverture
  • 233
  • 3
  • 10

2 Answers2

1

I think the issue is in following: library calls your callback (generic_NewFrame) after closing your form. You can fix it with few different ways.

First of all you can skip your callback method if your form already disposed:

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;
  }
  ...
}

Another approach is to wait and not to close you form till your library still working and wait in FormClosing or FormClosed event handler:

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

Or you can wait in your stop method:

override public void Stop()
{
  if (thread != null)
  {
    stopEvent.Set();
    thread.Join();
  }
}
Sergey Teplyakov
  • 11,477
  • 34
  • 49
  • Using thread.Join() prevents FormClosing from completion. Somehow, NewFrame on Worker thread never ends, no matter whether I check if pictureBox1.IsDisposed or not. – ZuOverture Dec 19 '12 at 03:53
  • Ok, it seems replacing Invoke with BeginInvoke solved this one. Honestly, wish to understand why ~_~ – ZuOverture Dec 19 '12 at 04:32
  • Invoke is a synchronous call. This means that you'll wait till the underlying body will executed. I strongly suggested to find out a root of the issue instead of using BeginInvoke. I assume that call to thread.Join() lead to deadlock, that's why form not closes. And what about additional check to IsDisposed? – Sergey Teplyakov Dec 19 '12 at 08:50
  • **if (this.IsDisposed) return;** - doubtful, since generic_NewFrame is not executed in UI thread. Maybe, it would work if I invoke whole generic_NewFrame on UI thread. It seems quite reasonable, since worker threads shouldn't be overloaded with anything besides their direct purpose. I'll try that, thanks. Though, unsure about how will race conditions change if NewFrame and FormClosing are executed on the same thread. – ZuOverture Dec 19 '12 at 09:19
0

To avoid the race condition that's causing this, you can do the following:

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

It's important that IsDisposed is checked on the UI thread, i.e. inside the delegate that's invoked.

Joe
  • 122,218
  • 32
  • 205
  • 338