canceling backgroundworker thread – how is the background process handled?

  • Thread starter Thread starter hdjim
  • Start date Start date
H

hdjim

When you cancel a backgroundworker thread via
backgroundWorker.CancelAsync() what happens to the process that was
running on that thread? Is the rug just pulled out from under it?
I’d like to have the process be notified that a cancel was issued by
the calling foreground thread and have the process running on the
background thread exit via code that’s written to handle such a cancel
event. Is that possible? If so, I'd appreciate some code examples.
If not, what good is the background worker if it could leave data
running in a background process in unpredictable state?

Thanks
hd
 
When you cancel a backgroundworker thread via
backgroundWorker.CancelAsync() what happens to the process that was
running on that thread? Is the rug just pulled out from under it?
I’d like to have the process be notified that a cancel was issued by
the calling foreground thread and have the process running on the
background thread exit via code that’s written to handle such a cancel
event. Is that possible? If so, I'd appreciate some code examples.
If not, what good is the background worker if it could leave data
running in a background process in unpredictable state?

Thanks
hd

It is totally up to you what happens when the CancelAsync method is
called. See the example here,
http://msdn.microsoft.com/en-us/library/system.componentmodel.backgroundworker.aspx,
and look at the routine long ComputeFibonacci(...).

The CancelAsync is simply a request, that may or may not be honored in a
timely manner.
 
When you cancel a backgroundworker thread via
backgroundWorker.CancelAsync() what happens to the process that was
running on that thread? Is the rug just pulled out from under it?
I’d like to have the process be notified that a cancel was issued by
the calling foreground thread and have the process running on the
background thread exit via code that’s written to handle such a cancel
event. Is that possible? If so, I'd appreciate some code examples.
If not, what good is the background worker if it could leave data
running in a background process in unpredictable state?

Thanks
hd
Here's some code that I've used that may help, it's VB, but can be
translated pretty easily

Sub Main
'Launch a new process...
calcProc = System.Diagnostics.Process.Start("calc.exe")
'If you don't set this, you won't get event notifications because
the events won't be raised.
calcProc.EnableRaisingEvents = True

'Stop the process...
'Killing a process can have serious ramifications, but is shown here
'for demonstration purposes only! Use: CloseMainWindow() instead.
If Not calcProc.HasExited Then
calcProc.Kill()
End If
End Sub

''' <summary>Method for handling the termination of the Windows
Calculator process</summary>
''' <remarks>In order to be able to handle process events, you must:
''' set the process's EnableRaisingEvents to true</remarks>
Private Sub calcProc_Exited(ByVal sender As Object, ByVal e As
System.EventArgs) Handles calcProc.Exited
Console.WriteLine("{0}...Windows Calculator Terminated...{0}", nl)
End Sub

-Scott
 
hdjim said:
When you cancel a backgroundworker thread via
backgroundWorker.CancelAsync() what happens to the process that was
running on that thread? Is the rug just pulled out from under it?

As Mike says, when you call CancelAsync(), it doesn't actually stop
anything. It simply sets a flag that your own code needs to check
periodically, and exit appropriately (i.e. return from your DoWork event
handler).
I’d like to have the process be notified that a cancel was issued by
the calling foreground thread and have the process running on the
background thread exit via code that’s written to handle such a cancel
event. Is that possible? [...]

That's exactly what you _have_ to do. Your background thread code won't
actually exit unless you specifically make it happen.

Pete
 
The CancelAsync is simply a request, that may or may not be honored in a timely manner.

Ok, I spent lots of time searching Google on the BackgroundWorker and
was able to write up code to handle the Canceling of a background
thread only to find out as Family Tree Mike pointed out - The
CancelAsync is simply a request, that may or may not be honored in a
timely manner.

I created a form that creates an instance of the BackgroundWorker and
passes it to a class library in which is the long running task (LRT).
I have a Cancel button on the form and when I click it, it sends a
CancelAsync. In the dll, in the LRT, I have a loop and thru each
iteration I check for CancellationPending and if it’s true I exit the
loop. I read where you can’t use break to exit so I exit the loop by
making the loop think it’s done. I tried both ways actually. No
difference I can tell.

It probably cancels immediately 7 out of 10 times, the other 3 the
form is “not responding”. The fact that CancelAsync is simply a
request, that may or may not be honored in a timely manner is a real
let down.

I got my hands on Andrew Troelsen’s book C# 2008 and the .NET 3.5
Platform and he points out (p. 601) "It is important to note that
foreground and background threads are not synonymous with primary and
worker threads."

So I'm asking all you experts, before I scrap all the BackgroundWorker
code and switch from The System.ComponentModel namespace to the The
System.Threading namespace and rather then create a "background
thread" via the BackgroundWorker create a "worker thread" via...days
of trial and error, swearing, wondering why things aren't working and
lots of ugly looking code using new Thread, can one cancel a process
running in a worker thread and have it be responsive 100% of the time
or will you still get that it may or may not be honored in a timely
manner behavior?

Thanks!
hd
 
Ok, I spent lots of time searching Google on the BackgroundWorker and
was able to write up code to handle the Canceling of a background
thread only to find out as Family Tree Mike pointed out - The
CancelAsync is simply a request, that may or may not be honored in a
timely manner.

I created a form that creates an instance of the BackgroundWorker and
passes it to a class library in which is the long running task (LRT).
I have a Cancel button on the form and when I click it, it sends a
CancelAsync. In the dll, in the LRT, I have a loop and thru each
iteration I check for CancellationPending and if it’s true I exit the
loop. I read where you can’t use break to exit so I exit the loop by
making the loop think it’s done. I tried both ways actually. No
difference I can tell.

It probably cancels immediately 7 out of 10 times, the other 3 the
form is “not responding”. The fact that CancelAsync is simply a
request, that may or may not be honored in a timely manner is a real
let down.

I got my hands on Andrew Troelsen’s book C# 2008 and the .NET 3.5
Platform and he points out (p. 601) "It is important to note that
foreground and background threads are not synonymous with primary and
worker threads."

So I'm asking all you experts, before I scrap all the BackgroundWorker
code and switch from The System.ComponentModel namespace to the The
System.Threading namespace and rather then create a "background
thread" via the BackgroundWorker create a "worker thread" via...days
of trial and error, swearing, wondering why things aren't working and
lots of ugly looking code using new Thread, can one cancel a process
running in a worker thread and have it be responsive 100% of the time
or will you still get that it may or may not be honored in a timely
manner behavior?

Thanks!
hd


It sounds like the loop takes some time per iteration. The apparent
breaking time when the cancel button is hit is going to require some
time if you only check the flag at a single point in your loop. You may
be near or far from the flag check point, leading to timing differences.
The key is to check the flag in as many places as appropriate.

In other words, if your LRT takes 10 seconds per loop, you may see lags
of as long as 9+ seconds, if you just missed a check. Only you can
determine what is practical as far as "exit checking". Checking each
line in the LRT is not the way to go, but checking frequently where you
think the time between checks is on the order of 250 ms is not bad.
There is no hard rule regarding this frequency. You need to make it so
the user doesn't get to the point of frustration...
 
Mike, thanks for responding again.

It’s just the opposite actually. Right now I’m just testing/
experimenting with the BackgroundWorker so I’m simulating a LRT by
just using a simple for loop. Below is the code. As you can see, it
runs thru the loop very fast.

public void DoSomeWork(BackgroundWorker bw, DoWorkEventArgs
bwArgs ) {

for (int i = 0; i < 100000; i++) {
if (bw.CancellationPending) {
bwArgs.Cancel = true;
i = 100001; //make loop think it's done
//break;
}
else {
// simulate success or failure using odd or even
int iResult = ((i % 2) == 0) ?
TransferResult_Success : TransferResult_Failed;
bw.ReportProgress(iResult);
}
}

}
 
Mike, thanks for responding again.

It’s just the opposite actually. Right now I’m just testing/
experimenting with the BackgroundWorker so I’m simulating a LRT by
just using a simple for loop. Below is the code. As you can see, it
runs thru the loop very fast.

public void DoSomeWork(BackgroundWorker bw, DoWorkEventArgs
bwArgs ) {

for (int i = 0; i< 100000; i++) {
if (bw.CancellationPending) {
bwArgs.Cancel = true;
i = 100001; //make loop think it's done
//break;
}
else {
// simulate success or failure using odd or even
int iResult = ((i % 2) == 0) ?
TransferResult_Success : TransferResult_Failed;
bw.ReportProgress(iResult);
}
}

}

I think you should change the iResult value to acually be a percentage.
This will be more informative. Also, I think you should put a sleep
in the loop, even if for 10ms. Pete may have a cleaner suggestion, but
that is what I normally would do. With this, the cancel happens
immediately for me.
 
ReportProgress uses it's ProgressChangedEventArgs which has a readonly
property called progressPercentage which is just an int. So I use it
to report true or false - 1 or 0 - back on the form indicating success
or failure of each LRT.

So you use a Sleep in production code? I guess a few nanoseconds
won't be noticeable....

hmmm....I'll try that and see if it makes a diff.
 
hdjim said:
Mike, thanks for responding again.

It’s just the opposite actually. Right now I’m just testing/
experimenting with the BackgroundWorker so I’m simulating a LRT by
just using a simple for loop. [...]

In theory, it should work just fine.

However, it turns out that the CancelAsync() method and
CancellationPending property rely on a private boolean field in the
BackgroundWorker class that is _not_ marked as "volatile".

I have not really looked too deeply into this; I only even realized it
when I saw someone else on MSDN point it out in the comments for the doc
page for BackgroundWorker. However, in _theory_ the lack of the
"volatile" attribute for the variable means that it could wind up
optimized out of code or otherwise fail for changes to it made in one
thread to be visible in another.

Without a concise-but-complete code example, it's hard to know for sure
whether you're running into something like that. I tried half-heartedly
when I first saw the issue to try to get it to break, and couldn't.
Even on a multi-core computer, cancellation seemed to work just fine.

But that doesn't mean there's no problem. Threading code bugs are hard
to work on precisely because a lot of buggy threading code works fine
99.9% of the time.

In your case, you can replace the CancelAsync() method and
CancellationPending property with your own volatile flag. Set it when
the user clicks the Cancel button, and check it in your DoWork event
handler. If that makes the problem go away, then you may well have
found a scenario that reproduces the potential problem in the
BackgroundWorker class. Otherwise, you have some other problem in your
code that you haven't shown. A concise-but-complete code example is
always important for asking questions, and would be especially so in
this case.

Pete
 
hdjim said:
Ok, I spent lots of time searching Google on the BackgroundWorker and
was able to write up code to handle the Canceling of a background
thread only to find out as Family Tree Mike pointed out - The
CancelAsync is simply a request, that may or may not be honored in a
timely manner.

Just to be clear: it will be honored in as timely a manner as _your_
code allows. BackgroundWorker itself should not delay notification in
any way (not counting the potential bug in the class I mention in my
other reply).
I created a form that creates an instance of the BackgroundWorker and
passes it to a class library in which is the long running task (LRT).
I have a Cancel button on the form and when I click it, it sends a
CancelAsync. In the dll, in the LRT, I have a loop and thru each
iteration I check for CancellationPending and if it’s true I exit the
loop. I read where you can’t use break to exit so I exit the loop by
making the loop think it’s done. I tried both ways actually. No
difference I can tell.

Either you misunderstood what was written, or the person writing that
you can't use "break" to exit is wrong. BackgroundWorker doesn't care
at all how you get out of the DoWork event handle. You can use a flag
for the loop, break out of it, return directly, or even meddle with the
loop variable as your code example does.
[...]
I got my hands on Andrew Troelsen’s book C# 2008 and the .NET 3.5
Platform and he points out (p. 601) "It is important to note that
foreground and background threads are not synonymous with primary and
worker threads."

This has nothing to do with whatever you're dealing with. "Foreground"
and "background" simply refer to whether a thread can keep the process
alive or not (foreground threads do, background threads don't…they are
forcibly terminated if there are no longer any more foreground threads
active in the process).

"Primary" and "worker" on the other hand refer to the roles of the
threads. Your primary thread will certainly be a foreground thread, but
you can have worker threads that are foreground threads as well. The
roles are distinguished mainly by the lifetime of each thread and what
kind of work it's doing.
So I'm asking all you experts, before I scrap all the BackgroundWorker
code and switch from The System.ComponentModel namespace to the The
System.Threading namespace and rather then create a "background
thread" via the BackgroundWorker create a "worker thread" via...days
of trial and error, swearing, wondering why things aren't working and
lots of ugly looking code using new Thread, can one cancel a process
running in a worker thread and have it be responsive 100% of the time
or will you still get that it may or may not be honored in a timely
manner behavior?

I have never see any problem doing so. If you want advice concerning
code you've written, you need to post a concise-but-complete code
example that shows us exactly what the code you've written is doing and
how it fails to achieve what goals you have.

Pete
 
Back
Top