ObjectDisposed when closing multithreaded app [duplicate] - c#

This question already has answers here:
Closed 10 years ago.
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();
}
}

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

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.

Related

What is the proper procedure to alert a thread the stop what it's doing and return before exiting?

What is the proper procedure to alert a running thread to stop what it's doing and return before exiting the application?
protected Thread T;
protected static ManualResetEvent mre = new ManualResetEvent(false);
protected static bool ThreadRunning = true;
public Form1()
{
InitializeComponent();
T = new Thread(ThreadFunc);
T.Start();
}
private void ThreadFunc()
{
while (ThreadRunning)
{
// Do stuff
Thread.Sleep(40);
}
mre.Set();
}
private void ExitButton_Click(object sender, EventArgs e)
{
ThreadRunning = false;
mre.WaitOne();
mre.Close();
Application.Exit();
}
Initially I had my code setup like the above. My thinking for how exit properly is as follows:
Set ThreadRunning = false so that the next time thread T checks that variable it knows to stop.
Call mre.WaitOne() to wait for thread T to say it's actually done via it calling mre.Set().
If so, then unblock and continue, dispose of mre (mre.Close()) and exit.
For some reason the above setup sometimes fails after the exit button is clicked and the whole form becomes inactive.
My new setup is below but doesn't seem entirely correct to me, for instance mre.Set() isn't going to wait for anything and Application.Exit() is immediately after it. I'm just waiting for it to fail like before, but so far it hasn't.
protected Thread T;
protected static ManualResetEvent mre = new ManualResetEvent(false);
protected static bool ThreadRunning = true;
public Form1()
{
InitializeComponent();
T = new Thread(ThreadFunc);
T.Start();
}
private void ThreadFunc()
{
while (ThreadRunning)
{
// Do stuff
Thread.Sleep(40);
}
mre.WaitOne();
mre.Close();
}
private void ExitButton_Click(object sender, EventArgs e)
{
ThreadRunning = false;
mre.Set();
Application.Exit();
}
UI hangs because you blocks UI thread with mre.WaitOne();.
If you need to wait until thread exits, you can use its IsAlive property and process application messages, you don't need application events for that:
while(_t.IsAlive)
Application.DoEvents();
There are 2 thread cancelation aproaches:
cooperative - code that is executed by thread knows that it could be cancelled and cancellation handled gracefuly, that's what you try to do here.
imperative - force thread to stop - call Thread.Abort or Interrupt, don't use that.
As #HansPassant mentioned, bool is not the best option because that compiler may optimize that and bool value could be cached and its change may not be handled by looping thread. You need to make it at least volatile or just refactor code to use CancellationSource.
Given what your thread is doing, perhaps BackgroundWorker, Timer or Producer/Consumer pattern is a better alternative to Thread, but I have too little context to recommend anything. Also it works well only if you have only 1 instance of Form1 in the application, if you have multiform application and user can open several Form1 forms, you'll have problems.
General recommendation, if you can work with instance level fields, please do that, don't use static.
Waiting for 40 msec between doing stuff creates no problems, but what if you had to wait 5 sec or more? Then canceling between each waiting would be problematic, and the right thing to do would be to cancel the awaiting itself. It is fairly easy to do it actually. Just replace Thread.Sleep(40) with Task.Delay(40, token).Wait() where token is a CancellationToken.
class Form1 : Form
{
protected readonly CancellationTokenSource _cts;
protected readonly Thread _thread;
public Form1()
{
InitializeComponent();
_cts = new CancellationTokenSource();
_thread = new Thread(ThreadFunc);
_thread.Start();
}
private void ThreadFunc()
{
try
{
while (true)
{
// Do stuff here
Task.Delay(40, _cts.Token).GetAwaiter().GetResult();
}
}
catch (OperationCanceledException)
{
// Ignore cancellation exception
}
}
private void ExitButton_Click(object sender, EventArgs e)
{
_cts.Cancel();
this.Visible = false; // Hide the form before blocking the UI
_thread.Join(5000); // Wait the thread to finish, but no more than 5 sec
this.Close();
}
}
Personally I would prefer to do the background job using a Task instead of a Thread, because it is more easily awaited without blocking the UI. This task would run lazily using thread-pool threads. The drawback is that the stuff that runs every 40 msec would not always run in the same thread, so I could have thread-safety issues to resolve.
class Form1 : Form
{
protected readonly CancellationTokenSource _cts;
protected readonly Task _task;
public Form1()
{
InitializeComponent();
_cts = new CancellationTokenSource();
_task = Task.Run(TaskFunc);
this.FormClosing += Form_FormClosing;
}
private async Task TaskFunc()
{
try
{
while (true)
{
// Do async stuff here, using _cts.Token if possible
// The stuff will run in thread-pool threads
await Task.Delay(40, _cts.Token).ConfigureAwait(false);
}
}
catch (OperationCanceledException)
{
// Ignore cancellation exception
}
}
private void ExitButton_Click(object sender, EventArgs e)
{
this.Close();
}
private async void Form_FormClosing(object sender, FormClosingEventArgs e)
{
if (_task == null || _task.IsCompleted) return;
e.Cancel = true;
_cts.Cancel();
this.Visible = false; // Or give feedback that the form is closing
var completedTask = await Task.WhenAny(_task, Task.Delay(5000));
if (completedTask != _task) Debug.WriteLine("Task refuses to die");
_task = null;
await Task.Yield(); // To ensure that Close won't be called synchronously
this.Close(); // After await we are back in the UI thread
}
}

C# How to stop running backgroundWorker without cancellationPending

is there any way to stop backgroundWorker thread without cancellationPending?
I have code like this:
DoWorkFunction
{
if(worker.cancellationPending == true) return; //this works great but
VeryLongTimeComputingFunc();//this function take a lot of time and if it starts i can't stop it with cancellationPending
...Do something
}
Is there any way to stop worker even if it started VeryLongTimeComputingFunc()?
Maybe you could fire an "CancelWorker" event in your "VeryLongTimeComputingFunc" and in the EventHandler you stop the BackgroundWorker with "worker.CancelAsync()".
This should work:
class BackgroundClass
{
public event EventHandler CancelWorker;
BackgroundWorker worker = new BackgroundWorker();
BackgroundClass()
{
CancelWorker += new EventHandler(BackgroundClass_CancelWorker);
}
void BackgroundClass_CancelWorker(object sender, EventArgs e)
{
worker.CancelAsync();
}
void RunBackgroundWorker()
{
worker.DoWork += (sender, args) =>
{
VeryLongTimeComputingFunction();
};
}
void VeryLongTimeComputingFunction()
{
if (CancelWorker != null)
{
CancelWorker(this, new EventArgs());
}
}
}
This would require that you can change something in the "VeryLongTimeComputingFunction()"
Assuming you can not add proper cancellation support inside VeryLongTimeComputingFunction, your best option is to save a reference to the BGW's thread and call Abort on it.
Keep in mind this is not generally recommended as it may involve a messy cleanup.
To be safe, you should catch any ThreadAbortedException raised in your long function.
private Thread bgThread;
void DoWorkFunction()
{
bgThread = Thread.CurrentThread;
try
{
VeryLongTimeComputingFunc();
}
catch (ThreadAbortedException e)
{
//do any necessary cleanup work.
bgThread = null;
}
}
void CancelBGW()
{
if (bgThread != null)
{
bgThread.Abort();
}
}
Depending on when and how CancelBGW is called, you may also need a lock around assignments of bgThread.

Backgroundworker not cancelling

So I have a webpage scraper that uses backgroundworker to process each page. I also want to mention that I'm using MVVM light framework.
Inside my MainViewModel Constructor I am initializing the backgroundworker:
backgroundWorker = new BackgroundWorker()
{
WorkerReportsProgress = true,
WorkerSupportsCancellation = true
};
backgroundWorker.DoWork += new DoWorkEventHandler(backgroundWorker_DoWork);
On the LoadCompleted event of a WebBrowser control I start the backgroundworker:
wb = sender; //sender is the webbrowser control
if (!backgroundWorker.IsBusy)
{
backgroundWorker.RunWorkerAsync();
}
My next two methods are DoWork And StopWork:
private System.Threading.AutoResetEvent _resetEvent = new System.Threading.AutoResetEvent(false);
private object wb;
void backgroundWorker_DoWork(object sender, DoWorkEventArgs e)
{
BackgroundWorker wk = sender as BackgroundWorker;
if (wb != null)
{
FetchPage(wb);
if (wk.CancellationPending)
{
MessageBox.Show("Cancellation pending!");
}
_resetEvent.Set();
}
}
private void StopWork(object sender)
{
backgroundWorker.CancelAsync();
_resetEvent.WaitOne();
}
The fetchpage method will grab the sourcecode of the webbrowser control and start parsing it for content.
Inside of FetchPage I'm using BeginInvoke to update my UI thread:
Application.Current.Dispatcher.BeginInvoke(DispatcherPriority.Background, new Action(
() =>
{ ... }
My issue:
When I hit the Cancel button the StopWork method get's invoked, the cancel property on the backgroundWorker is set correctly to true, but the app just keeps going on. My if (wk.CancellationPending) is always false.
Any idea on what am I doing wrong here? I looked at tons of examples online and here on StackOverflow and they all state the same things that i already done.
Thanks.
EDIT:
After Ernos reply I tried passing the CancellationPending property to the FetchPage method and check for it in different locations, but it did not stop the processing.
void backgroundWorker_DoWork(object sender, DoWorkEventArgs e)
{
BackgroundWorker wk = sender as BackgroundWorker;
if (wb != null)
{
FetchPage(wb, wk.CancellationPending);
_resetEvent.Set();
}
}
Inside of FetchPage I'm using BeginInvoke to update my UI thread:
private void FetchPage(object sender, bool stopAll)
{
if (stopAll)
{
return;
}
Application.Current.Dispatcher.BeginInvoke(DispatcherPriority.Background, new Action(
() =>
{ ... }
What I have tried and worked was:
private bool stopAllWork = false;
...
private void StopWork(object sender)
{
stopAllWork = true;
backgroundWorker.CancelAsync();
_resetEvent.WaitOne();
}
and then inside DoWork:
void backgroundWorker_DoWork(object sender, DoWorkEventArgs e)
{
BackgroundWorker wk = sender as BackgroundWorker;
if (wb != null)
{
FetchPage(wb, stopAllWork);
_resetEvent.Set();
}
}
Now, because of this implementation my concern is if there will be any rogue backgroundWorkers remaining?
You need to evaluate the CancellationPending inside the FetchPage method.
You are checking it AFTER the load of work.
Erno is correct. You are checking if it is cancelled after you have done all of the work. To keep things modular, you may consider not passing the background worker to FetchPage; rather pass a function that returns if you should cancel.
public void FetchPage(WebBrowser wb, Func<bool> cancelNow)
{
...
if(cancelNow()) {
return;
}
...
}
You would call it like so
FetchPage(wb, () => wk.CancellationPending);
but you could put that function in another application that does not use a background worker and call it like so:
FetchPage(wb, () => false);
Note: make sure you are checking if you should cancel as the work is being completed. For example if most of the work happens in a loop, check inside the loop. If there are a series of steps, check between each step.

Background worker cancelation

I have a timer and a background worker, which initiates on every timer tick. Sometimes I need for user to stop this workflow and call that same bg worker from a button click.
Since it is in Silverlight it is all async. BG worker make async Webservice call, which asynchronously returns data.
I have done this, but it just feels wrong. What are best ways to handle that kind of situation?
button_click_event(..)
{
_loadTimer.Stop();
_worker.CancelAsync();
_worker.RunWorkerAsync();
}
WebService call
public void GetUserStats(DateTime start, DateTime end, Action<IEnumerable<IUserStats>, Exception> callback)
{
_context.GetUserStatsCompleted += ContextGetUserStatsCompleted;
_context.GetUserStatsAsync(start,end,callback);
}
void ContextGetUserStatsCompleted(object sender, GetUserStatsCompletedEventArgs e)
{
var callback = e.UserState as Action<IEnumerable<IUserStats>, Exception>;
Exception error = null;
var result = new ObservableCollection<IUserStats>();
if (e.Error == null)
{
result = e.Result;
}
else
{
error = e.Error;
}
_context.GetUserStatsCompleted -= ContextGetUserStatsCompleted;
callback(result, error);
}
and my worker
void WorkerDoWork(object sender, DoWorkEventArgs e)
{
TicketService.GetUserStats(StartDate, EndDate, (result, error) =>
{
StreamHolder = result;
});
}
Firs of all, you should always check to see if your worker is running, prior to attempting to run it again. If you don't then it's possible that your application will throw an exception.
if(!_worker.IsBusy)
{
_worker.RunWorkerAsync();
}
Second of all, just calling CancelAsync() is not enough to cancel the current operation of the background worker. You will have to add code to the background worker's DoWork event handler. (In your case WorkerDoWork)
if(_worker.CancelationPending == true)
{
e.Cancel = true;
return;
}
You can read more about the proper way to use a background worker here:
http://msdn.microsoft.com/en-us/library/cc221403(v=vs.95).aspx

How to raise cross thread event

How can I raise the the event GeocodeAddressEventHandler from another thread?
System.Threading.Thread MapThread;
WinformMap map ;
public delegate void GeocodeAddressEventHandler(object sender, EventArgs e);
public event GeocodeAddressEventHandler GeocodeAddressEvent;
//How to Raise this Event from another thread??
public void GeocodeAddress_Raised(object sender, EventArgs e)
{
MapLib.GeocodeAddress("12798 1ST ST", "", "", "");
}
public bool LoadMap(string restorepoint)
{
////////////////////////Engine Controls////////////////////////////////////////////////////////
try
{
System.ServiceModel.OperationContext context = System.ServiceModel.OperationContext.Current;
//This is to instantiate a winform from a Console (will convert to service shortly)
MapThread = new System.Threading.Thread(new System.Threading.ThreadStart(delegate
{
using (System.ServiceModel.OperationContextScope scope = new System.ServiceModel.OperationContextScope(context))
{
this.GeocodeAddressEvent += new GeocodeAddressEventHandler(GeocodeAddress_Raised);
}
}));
MapThread.SetApartmentState(System.Threading.ApartmentState.STA);
MapThread.Start();
MapThread.Join();
}
catch (Exception ex)
{
return false;
}
return true;
}
Actually it turned out that the thread was terminating after the scope of the delegate terminated. This might be a dumb way to do it, but I put a while Queue.empty { sleep } in that scope so it never terminated, then I launched the LoadMap from yet another thread, so that it jam up my WCF service waiting for the neverending queue to terminate.
Take a look at
http://www.codeproject.com/KB/cs/Cross_thread_Events.aspx
See also BackgroundWorker class : http://msdn.microsoft.com/en-us/library/system.componentmodel.backgroundworker.aspx
Take a look at the Invoke() method:
http://msdn.microsoft.com/library/zyzhdc6b.aspx

Categories

Resources