Need help to stop the BackgroundWorker thread - c#

Need help to stop the BackgroundWorker thread.
I am trying to stop a background worker thread. This is what I am doing:
On stop button click (UI layer):
if (backgroundWorker.IsBusy == true &&
backgroundWorker.WorkerSupportsCancellation == true)
{
backgroundWorker.CancelAsync();
}
On DoWork event (UI layer):
if ((backgroundWorker.CancellationPending == true))
{
e.Cancel = true;
}
else
{
//Function which progresses the progress bar is called
//here with its definition in business layer
}
Once the DoWork event is fired and my program control is in the function defined in Business layer, how do I revert back to the DoWork event to set ‘e.Cancel = true’?

Setting e.Cancel does nothing, if CancellationPending is true you need to basically break out of DoWork() using return or whatever (after you've stopped what you're doing).
Something like:
private void DoWork(...)
{
// An infinite loop of work!
while (true)
{
// If set to cancel, break the loop
if (worker.CancellationPending)
break;
// Sleep for a bit (do work)
Thread.Sleep(100);
}
}
DoWork() executes in a seperate thread to the UI thread, you can report back to the UI thread using BackgroundWorkr.ReportProgress().

DoWork will run in it's own thread and is not dependant of the GUI thread.
You do almost everything correct. From the GUI thread, set the CancellationPending to true.
In the DoWork method, you probably have a loop of some sort.
Here you check if CancellationPending == true, but in addition to setting e.Cancel to true, also include a return call to make the method return and effectively stopping the worker. This also causes the WorkerCompleted event to fire on the GUI thread if the method is hooked up.
If the DoWork method perform some long task that is not divided into parts (for example if your DoWork method looks like this:
void DoWork( (object sender, DoWorkEventArgs e)
{
myClass.SomeLongOperation();
}
Then you are out of luck, since you need to manually check the CancellationPending inside the DoWork method to be able to stop it. If the DoWork itself "hangs" and waits for a operation you can't control, you can't stop it (in any orderly fashion) by setting CancellationPending from the GUI thread.

Once the DoWork event is fired and my program control is in the function defined in Business layer, how do I revert back to the DoWork event to set ‘e.Cancel = true’?
You don't. If you want cancellation to be possible during execution of your business layer, then your business layer must support cancellation. So, you have two options:
In your DoWork method, call only short-time business layer methods and check for CancellationPending in between.
Make your business layer methods cancellation-aware, i.e., pass the BackgroundWorker to them and have them periodically check CancellationPending (and retun, once it turns true).

Keep checking the CancellationPending=True Flag in the else part of your logic, and return when true.

The code e.Cancel = true only sets a state on the BackgroundWorker, so that it knows it has been cancelled, it doesn't actually cancel the process.
You'll have to check the CancellationPending inside the loop of your method and break it or return.
void DoWork(object sender, DoWorkEventArgs e) {
for (int i = 0; i < length; i++) {
if(e.CancellationPending) {
return;
}
// Long running code
}
}

Related

How can I check if previously running ProgressChanged has finished in DoWork event in c#

Here is a scenario, its a winforms application where I have a collection of processes which i'm running one by one inside a for-loop in the DoWork event of Backgroundworker class. And I call ReportProgress() periodically in the for-loop to update UI.
Now when I call ReportProgress() it triggers ProgressChanged event where I have the code to update the UI with all the message I have set previously in DoWork. But as this runs in a separate thread, the control goes in parallel in DoWork, but I want to hold/wait the for-loop for sometime until the previously running ProgressChanged event.
Currently, I'm calling Thread.Sleep(1000) in for-loop before executing any operation in it (like picking up the next process and running it) and this code is giving me desired output. So I just wanted to check if there is any alternative solution where (I don't use Thread.Sleep instead) I can verify/ wait the for-loop until the previously running ProgressChanged event has finished its job, and only then I proceed to run the next process from the collection in for-loop.
Without addressing the issues with your overall design, the way you would do this is with a semaphore. The most basic example would be:
static Semaphore _semaphore = new Semaphore(0,1);
WorkStatus _workStatus;
void DoWork()
{
_semaphore.WaitOne();
try
{
_workStatus = Foo(); //Only one thread will be able to execute this at a time
}
finally
{
_semaphore.Release();
}
}
void HandleProgressChanged()
{
_semaphore.WaitOne(); //Wait for any DoWork threads to finish
try
{
DisplayProgress(_workStatus);
}
finally
{
_semaphore.Release();
}
Task.Run(DoWork);
}

SQL Query inside BackgroundWorker

I am looking for implementation of background worker and progress bar. All I can find is a simulation using the Threading.Sleep(). Samples are all working but its not working if change the simulation to actual SQL query.
Where should I insert the query in below code, please help. .NET-2.0
void m_oWorker_DoWork(object sender, DoWorkEventArgs e)
{
// The sender is the BackgroundWorker object we need it to
// report progress and check for cancellation.
//NOTE : Never play with the UI thread here...
for (int i = 0; i < 100; i++)
{
Thread.Sleep(100);
// Periodically report progress to the main thread so that it can
// update the UI. In most cases you'll just need to send an
// integer that will update a ProgressBar
m_oWorker.ReportProgress(i);
// Periodically check if a cancellation request is pending.
// If the user clicks cancel the line
// m_AsyncWorker.CancelAsync(); if ran above. This
// sets the CancellationPending to true.
// You must check this flag in here and react to it.
// We react to it by setting e.Cancel to true and leaving
if (m_oWorker.CancellationPending)
{
// Set the e.Cancel flag so that the WorkerCompleted event
// knows that the process was cancelled.
e.Cancel = true;
m_oWorker.ReportProgress(0);
return;
}
}
//Report 100% completion on operation completed
m_oWorker.ReportProgress(100);
}
By "the query", it sounds like you only have one operation to do. This makes a progress bar tricky, since there is no way of really measuring the progress of an SQL query. It can't tell you how much longer it is going to be. You might just want to use the non-committal infinite scrolling busy indicator while you perform the query.
You should execute any sql query, which is the real task of this background worker, just after checking for CancellationPending.

BackgroundWorker - CancellationPending changing to false in RunWorkerCompleted. Why?

After canceling the BackGroundWorker, in the DoWork, the CancellationPending is true but when he comes to the RunWorkerCompleted, the CancellationPending is false. I dont know what did I do wrong?
static BackgroundWorker b1;
static void Main(string[] args)
{
b1=new BackgroundWorker();
b1.DoWork += new DoWorkEventHandler(work1);
b1.RunWorkerCompleted += new RunWorkerCompletedEventHandler(completed);
b1.WorkerSupportsCancellation = true;
b1.RunWorkerAsync("Hellow");
Console.ReadLine();
}
private static void completed(object sender, RunWorkerCompletedEventArgs e)
{
if (((BackgroundWorker)sender).CancellationPending)
Console.WriteLine("Canceled!");
else
Console.WriteLine("Result:" + e.Result);//it goes here every time
}
private static void work1(object sender, DoWorkEventArgs e)
{
((BackgroundWorker)sender).CancelAsync();
if (((BackgroundWorker)sender).CancellationPending)
{
e.Cancel = true;
}
}
By the way, How can I add an error that occur in the DoWork to the RunWorkerCompletedEventArgs.Error for shoing it up to the user?
Yes, the BackgroundWorker class sets the CancellationPending property to false before raising the RunWorkerCompleted event. Whether or not the worker was actually cancelled.
This is quite intentional, it stops you from falling into a nasty trap that's always around when you use threads. Code that uses threads often misbehaves randomly and unpredictably due to a kind of bug called "threading race". It is a very common kind of bug and dastardly difficult to debug.
What can easily go wrong in your intended approach if BGW didn't do this is that you'll assume that the worker got cancelled when you see CancellationPending set to true. But that's an illusion, you cannot tell the difference between it being cancelled and it completing normally. The corner case is you calling CancelAsync() a microsecond before the worker completes. The worker never has a chance to even see the CancellationPending flag set to true, it was busy finishing the last bits of the DoWork event handler method. That's a threading race, the worker raced ahead of your call and completed normally.
The proper hand-shake that avoids this bug is your worker setting e.Cancel to true when it sees the CancellationPending property set to true. And of course stopping what's its doing. Now it is reliable, the e.Cancelled property in the RunWorkerCompleted event handler is a copy of e.Cancel. So your code can now reliably tell you whether or not the worker saw the cancel request.
I believe the CancellationPending property is for use during the background operation (in your work1 method). It will tell the background worker that you have requested the background operation be canceled. Once the RunWorkerCompleted event is called, the background worker has done the work to cancel the request, and therefore the cancellation is no longer pending.
EDIT: the RunWorkerCompletedEventArgs has a Cancelled property that will tell you if the background operation was cancelled.
If you throw an exception from the DoWork method (work1 in your case), it should be caught by the BackgroundWorker and populate the Error property of the RunWorkerCompletedEventArgs.

Canceling Worker threads in winforms

I've got two list boxes, one a master, the other a child. When the index changes on the master, the child listbox gets filled appropriately with records relating to the master. My problem is coming up when one master takes a long time to get all the records and before it has completed getting the records, a user clicks a different master that takes less time to fill. What happens is that eventually, the master that was taking longer fills in the child list box even though the user isn't on that master anymore.
I've been using BackgroundWorker threads to do the filling.
bw_LoadAll.DoWork += new DoWorkEventHandler(bg_LoadAllWork);
bw_LoadAll.RunWorkerCompleted += new RunWorkerCompletedEventHandler(bg_LoadAllWorkCompleted);
bw_LoadAll.WorkerSupportsCancellation = true;
I subscribed to the SelectedIndexChanged event for the master and I set the canceled equal to true:
bw_LoadAll.CancelAsync();
Here is the code in the DoWork method:
List<object> s = Repository.Instance().LoadAll();
if (!bw_LoadAll.CancellationPending) {
e.Result = s;
} else {
e.Cancel = true;
}
But for some reason, the code for the worker completed keeps getting called. Here is the worker completed code:
if (!e.Cancelled) {
ddl.DataSource = e.Result;
ddl.DisplayMember = "QuickName";
ddl.ValueMember = "ID";
}
Is there something else I have to do to cancel this thread from returning?
Your method, bg_LoadAllWork, should be defined as:
void bg_LoadAllWork(object sender, DoWorkEventArgs e)
{
// Do your work here...
}
Inside of bg_LoadAllWork, you need to check for e.CancellationPending, and if it's true, set e.Cancel = true;
This last part is important - if you never set e.Cancel, then your "e.Cancelled" will never equal true. The call to CancelAsync doesn't actually cancel anything - it's more like "Request that the background work cancel itself" - you have to put the logic in place to cause the cancellation.
From CodeProject in your do_work function you need to check for CancellationPending on the worker thread then set the DoWorkEventArgs.Cancel variable to true.
It's been a while since I've used a BackgroundWorker, but if I memory serves, when you call bw_LoadAll.CancelAsync, there's no actual abortion of your LoadAllWork method unless LoadAllWork checks bw_LoadAll.CancelationPending.
Confirmed by http://msdn.microsoft.com/en-us/library/system.componentmodel.backgroundworker.cancelasync.aspx : "CancelAsync submits a request to terminate the pending background operation and sets the CancellationPending property to true.
When you call CancelAsync, your worker method has an opportunity to stop its execution and exit. The worker code should periodically check the CancellationPending property to see if it has been set to true."
So, in your SelectedIndexChanged event handler, when you call bw_LoadAll.CancelAsync(), it's setting bw_LoadAll.CancelationPending to true, but it's not actually aborting the LoadAllWork method. The slow-loader is going to still finish.

In C#, wait on the mainthread while continuing to process UI updates? (.NET 2.0 CF)

I want to otherwise block code execution on the main thread while still allowing UI changes to be displayed.
I tried to come up with a simplified example version of what I'm trying to do; and this is the best I could come up with. Obviously it doesn't demonstrate the behavior I'm wanting or I wouldn't be posting the question. I just hope it gives some code context to back my poor explanation of the problem I'm hoping to solve.
Within a button click handler on a form I have this:
private void button2_Click(object sender, EventArgs e)
{
AutoResetEvent autoResetEvent = new AutoResetEvent(false);
new Thread(delegate()
{
// do something that takes a while.
Thread.Sleep(1000);
// Update UI w/BeginInvoke
this.BeginInvoke(new ThreadStart(
delegate() {
this.Text = "Working... 1";
this.Refresh();
Thread.Sleep(1000); // gimme a chance to see the new text
}));
// do something else that takes a while.
Thread.Sleep(1000);
// Update UI w/Invoke
this.Invoke(new ThreadStart(
delegate() {
this.Text = "Working... 2";
this.Refresh();
Thread.Sleep(1000); // gimme a chance to see the new text
}));
// do something else that takes a while.
Thread.Sleep(1000);
autoResetEvent.Set();
}).Start();
// I want the UI to update during this 4 seconds, even though I'm
// blocking the mainthread
if (autoResetEvent.WaitOne(4000, false))
{
this.Text = "Event Signalled";
}
else
{
this.Text = "Event Wait Timeout";
}
Thread.Sleep(1000); // gimme a chance to see the new text
this.Refresh();
}
If I didn't set a timout on the WaitOne() the app would deadlock on the Invoke() call.
As to why I'd want to do this, I've been tasked with moving one subsystem of an app to do work in a background thread, but still have it block user's workflow (the main thread) only sometimes and for certain types of work related to that subsystem only.
You want to use the "BackgroundWorker" class, which will take most of this pain out of this for you.. but as mentioned before, you'll also want to structure it so that the main thread is updating the UI and the worker is doing the heavy lifting.
It is easyer then you might think.
Suggestion: when you need a thread to perform some occasional work, get it from the threadpool, so you will not need strange/error prone recycling code.
When you want something on another thread to update your UI, you just need a reference to the form and to call Form.Invoke passing the UI code you want the main thread to execute; it's a best pactice, in an event, to release the UI thread as soon as possible.
Ie:
private void button1_Click(object sender, EventArgs e)
{
// this is the UI thread
ThreadPool.QueueUserWorkItem(delegate(object state)
{
// this is the background thread
// get the job done
Thread.Sleep(5000);
int result = 2 + 2;
// next call is to the Invoke method of the form
this.Invoke(new Action<int>(delegate(int res)
{
// this is the UI thread
// update it!
label1.Text = res.ToString();
}), result);
});
}
Hope this helps you:)
EDIT: I am sorry, I didn't read the "blocking user workflow" part.
WindowsForms is not designed to do that, blocking the main thread is BAD (it handles the messages from the OS).
You don't have to block the user workflow via freezing a form (which would then be considered "Not Responding" by windows), the way to block user workflow is by disabling any control you want (with the Invoke method above if from another thread), even the entire form!!
Common activities which 'block' the main thread are things like opening messages boxes or modal dialog. The main code appears to block at the MessageBox or ShowDialog call.
The way those items work (and MessageBox is just a specialized modal dialog) is that they contain their own message pump while they're blocking.
Although it's a nasty hack, you can do something like this in your app by looping calling Application.DoEvents() to keep the user messages pumping while you're waiting for your other task to complete. You need to be careful because all sorts of nasty things might lead from pumping messages like this - for example someone close the form or reenter your current message handler - the modal dialogs avoid this by effectively disabling input from the form which launches them.
I did mean to say that BackgroundWorker is a better solution, if you can make it fit. I sometimes combine it with a modal 'progress dialog' to give me the background thread / message pumping and the blocking of the UI thread.
Edit - to expand on the last bit:
One approach I've used is to have a 'progress form' class, which takes a BackgroundWorker object as a constructor parameter, and contains handlers for the progress and completion events of the background worker which gets passed to it.
The form which wants the work done creates the background worker and hooks up the 'work' event (can't remember what it's called right now), and then creates a progress dialog to which it passes the background worker. It then modally shows the progress dialog, which means it will wait (but pumping messages) until the progress dialog closes.
The progress form is responsible for starting the BackgroundWorker from its OnLoad override, and closes itself when it sees the BackgroundWorker complete. Obviously you can add message text, progress bars, cancel buttons, whatever to the progress form.
structure your app so that the main thread only performs UI updates, and all other work is done on secondary threads via a work queue; then add a waiting-for-godot flag to your main thread and use it to guard the method that adds items to the work queue
out of curiosity: why do you want to do this?
You should probably restructure your code as others have suggested, but depending on the behavior you're looking for, you might also want to have a look at using a Thread.Join on your background worker thread. Join actually allows the calling thread to process COM and SendMessage events while it waits for the other thread to finish. This seems like it could be dangerous in come cases, but I've actually had a couple scenarios where it was the only way to wait for another thread to finish cleanly.
Thread..::.Join Method
Blocks the calling thread until a
thread terminates, while continuing to
perform standard COM and SendMessage
pumping.
(from http://msdn.microsoft.com/en-us/library/95hbf2ta.aspx)
I agree with the others that are suggesting you use Background Worker. It does the heavy lifting and allows the UI to continue. You can use the Report Progress of Background Worker to initiate times where the Main Form can be set to be disabled while it performs the actions in the background and then re-enable once the 'certain instances' have completed processing.
Let me know if this helps!
JFV
If you could adjust your code so that you set a flag once a process has begun and then check that in the UI before you start an additional operation I think you'd have a much easier time coding this. I would create a delegate that could be called from the thread in the threadpool or user created thread to update on progress in the UI. Once the background process has been completed switch the flag and now normal UI operations can continue. The only caveat you need to be aware of is that when you update UI components you must do it on the thread they were created on, the main/UI thread. In order to accomplish this you can call the Invoke() method on any control that lives on that thread and pass it the delegate and parameters you need to call it.
Here's a link to a tutorial I wrote some time ago about how to use Control.Invoke():
http://xsdev.net/tutorials/pop3fetcher/
Just a code snippet: don't have much time sorry :)
private void StartMyDoSomethingThread() {
Thread d = new Thread(new ThreadStart(DoSomething));
d.Start();
}
private void DoSomething() {
Thread.Sleep(1000);
ReportBack("I'm still working");
Thread.Sleep(1000);
ReportBack("I'm done");
}
private void ReportBack(string p) {
if (this.InvokeRequired) {
this.Invoke(new Action<string>(ReportBack), new object[] { p });
return;
}
this.Text = p;
}
It is best to dispatch the work but if you must, maybe something like this. Just call this method to wait for the signal rather than calling the waitone.
private static TimeSpan InfiniteTimeout = TimeSpan.FromMilliseconds(-1);
private const Int32 MAX_WAIT = 100;
public static bool Wait(WaitHandle handle, TimeSpan timeout)
{
Int32 expireTicks;
bool signaled;
Int32 waitTime;
bool exitLoop;
// guard the inputs
if (handle == null) {
throw new ArgumentNullException("handle");
}
else if ((handle.SafeWaitHandle.IsClosed)) {
throw new ArgumentException("closed wait handle", "handle");
}
else if ((handle.SafeWaitHandle.IsInvalid)) {
throw new ArgumentException("invalid wait handle", "handle");
}
else if ((timeout < InfiniteTimeout)) {
throw new ArgumentException("invalid timeout <-1", "timeout");
}
// wait for the signal
expireTicks = (int)Environment.TickCount + timeout.TotalMilliseconds;
do {
if (timeout.Equals(InfiniteTimeout)) {
waitTime = MAX_WAIT;
}
else {
waitTime = (expireTicks - Environment.TickCount);
if (waitTime <= 0) {
exitLoop = true;
waitTime = 0;
}
else if (waitTime > MAX_WAIT) {
waitTime = MAX_WAIT;
}
}
if ((handle.SafeWaitHandle.IsClosed)) {
exitLoop = true;
}
else if (handle.WaitOne(waitTime, false)) {
exitLoop = true;
signaled = true;
}
else {
if (Application.MessageLoop) {
Application.DoEvents();
}
else {
Thread.Sleep(1);
}
}
}
while (!exitLoop);
return signaled;
}
I went with something I haven't seen posted yet which is to use MessageQueues.
The MainThread blocks while waiting for the next message on a queue.
The background thread posts different types of messages to the MessageQueue.
Some of the message types signal the MainThread to update UI elements.
Of course, there is a message to tell the MainThread to stop blocking and waiting for messages.
Seems over the top considering the windows message loop already exists somewhere, but it works.

Categories

Resources