ObjectDisposed при закрытии многопоточного приложения
Возможный дубликат:
Как остановить BackgroundWorker на закрытии формы?
** Что касается возможного дублирования - методы BackgroundWorker здесь не применимы.
Ниже моя попытка использовать библиотеку AForge для получения видео с IP-камер.
Предполагается, что каждый видеопоток выполняется в отдельном потоке, уведомляя поток пользовательского интерфейса о поступлении нового кадра. Обработчик событий выполняется в том же потоке, который его вызвал, поэтому мне нужно использовать 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();
}
}
2 ответа
Я думаю, что проблема заключается в следующем: библиотека вызывает ваш обратный вызов (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;
}
...
}
Другой подход заключается в том, чтобы подождать, а не закрывать форму, пока ваша библиотека все еще работает, и ждать в FormClosing
или же FormClosed
обработчик события:
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;
}
}));
Важно что IsDisposed
проверяется в потоке пользовательского интерфейса, то есть внутри вызываемого делегата.