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.
Related
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.
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.
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
I have noticed that as the database of my application has grown, the time taken to return results has also increased. In the beginning this was negligible because it was such a small amount of time to return the data source.
Now I am at the point where it temporarily makes the UI unresponsive for a couple of seconds, but I would like to create background workers to do these tasks.
The problem with creating these, is that there are around 9 buttons that would need a background worker and all they do is call a different method in the DLL. Is there any way to use a common method to create these background workers using the API for background workers or should I create an Enum that corresponds to each button and is a parameter taken in by the method that constructs the background worker. Thus meaning I could use a simple switch to execute whatever method from the DLL I choose?
Sample Code:
void bg_DoWorkImports(object sender, DoWorkEventArgs e)
{
BackgroundWorker worker = sender as BackgroundWorker;
try
{
e.Result = EngineBllUtility.GetNotImportedFiles(connectionString);
}
catch (Exception ex)
{
MessageBox.Show(ex.Message);
}
}
void bg_RunWorkerCompletedImports(object sender, RunWorkerCompletedEventArgs e)
{
DataSet temp = (DataSet)e.Result;
if (e.Result != null)
{
importFileGridView.DataSource = temp.Tables[0];
}
}
You could pass an Func<T> to a method that creates a BackgroundWorker and call that action from within to DoWork-event.
Something like this
public class BackgroundWrapper<T>
{
private Func<T> workMethod;
private Action<T> completeMethod;
public static void StartBackgroundworker(Func<T> workMethod, Action<T> completeMethod)
{
BackgroundWrapper<T> bWrap = new BackgroundWrapper<T>();
bWrap.workMethod = workMethod;
bWrap.completeMethod = completeMethod;
bWrap.Start();
}
private void Start()
{
BackgroundWorker bw = new BackgroundWorker();
bw.DoWork += new DoWorkEventHandler(bw_DoWork);
bw.RunWorkerCompleted += new RunWorkerCompletedEventHandler(bw_RunWorkerCompleted);
bw.RunWorkerAsync();
}
void bw_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
{
completeMethod((T)e.Result);
}
void bw_DoWork(object sender, DoWorkEventArgs e)
{
e.Result = workMethod();
}
}
Instead of using BackgroundWorker, an alternative would be to use the TPL. This would let you write the code directly within each member:
void buttonImport_Click(object sender, DoWorkEventArgs e)
{
Task.Factory
.StartNew( () => return EngineBllUtility.GetNotImportedFiles(connectionString))
.ContinueWith( t =>
{
try
{
if (t.Result != null)
{
importFileGridView.DataSource = t.Result.Tables[0];
}
}
catch (AggregateException ex)
{
MessageBox.Show(ex.InnerException.Message);
}
}, TaskScheduler.FromCurrentSynchronizationContext());
}
Sure, I don't see why you couldn't create a "switchboard" sort of function for that. In fact, you might want to do that, because it would make things a little more modular and promote code reuse.
As far as enums go, personally, I create classes to pass lots of arguments in and out of such things.
I think you need to build some kind of queuing mechanism where one background worker picks up each of the button click jobs and kicks off one after other.
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