471,349 Members | 1,260 Online
Bytes | Software Development & Data Engineering Community
Post +

Home Posts Topics Members FAQ

Join Bytes to post your question to a community of 471,349 software developers and data experts.

Cancel BackgroundWorker

How do you cancel a BackgroundWorker?

For this BackgroundWorker:

bgWorker.WorkerReportsProgress = true;
bgWorker.WorkerSupportsCancellation = true;

I have tried this, but it never exits the do...while loop:

if (bgWorker.IsBusy == true) {
bgWorker.CancelAsync();
}
do {
Thread.Sleep(0);
} while (bgWorker.IsBusy == true);

I tried removing the do...while loop, but then I get errors because the
thread is still being used!
Sep 11 '08 #1
7 4622
On Thu, 11 Sep 2008 11:24:00 -0700, jp2msft
<jp*****@discussions.microsoft.comwrote:
How do you cancel a BackgroundWorker?
See the MSDN documentation for the class. It includes an example of a
cancelable BackgroundWorker.

The short story: your DoWork handler needs to take into account the
possibility of cancellation, checking for that condition at opportune
moments during processing and terminating early when canceled.

From the docs for BackgroundWorker.CancelAsync():

The worker code should periodically check the
CancellationPending property to see if it has
been set to true.

Pete
Sep 11 '08 #2
I copied their example, and I even have my thread checking to see if it has
been cancelled within its loops:

if (worker.CancellationPending == true) {
e.Cancel = true;
return;
}

This part of the code is triggered, but when this happens, the code does not
enter the bgWorker_ProgressChanged or bgWorker_RunWorkerCompleted event
handlers.

Because of this, my do...while loop never exits.

How is this fixed?

BTW: Thanks for your help, too!

"Peter Duniho" wrote:
See the MSDN documentation for the class. It includes an example of a
cancelable BackgroundWorker.

The short story: your DoWork handler needs to take into account the
possibility of cancellation, checking for that condition at opportune
moments during processing and terminating early when canceled.

From the docs for BackgroundWorker.CancelAsync():

The worker code should periodically check the
CancellationPending property to see if it has
been set to true.

Pete
Sep 11 '08 #3
On Thu, 11 Sep 2008 13:17:01 -0700, jp2msft
<jp*****@discussions.microsoft.comwrote:
I copied their example, and I even have my thread checking to see if it
has
been cancelled within its loops:

if (worker.CancellationPending == true) {
e.Cancel = true;
return;
}

This part of the code is triggered, but when this happens, the code does
not
enter the bgWorker_ProgressChanged or bgWorker_RunWorkerCompleted event
handlers.

Because of this, my do...while loop never exits. [...]
Unless you post a concise-but-complete code sample that demonsrates
exactly what you're doing, it's not possible to say what you're doing
wrong. The RunWorkerCompleted event definitely should be raised, but
there's nothing in the code you post that proves that you're handling it,
never mind that it would affect the loop you posted.

The most likely explanation is that the loop you posted is executing on
the main GUI thread, and of course doing so would cause all sorts of
issues, including this one. But without all the code, it's impossible to
say for sure what's going on.

That said, I'll point out that just from the little code you've posted,
it's clear that your basic strategy is simply wrong. There is no sense in
handing off work to a different thread only to have the current thread sit
and wait for it to be done. There also is _especially_ no sense in
writing a "busy wait" as you've done here, where the only thing the loop
does is repeatedly check for a specific condition. Even in situations
where it makes sense for a thread to wait for something to happen (and
this doesn't appear to be such a situation), there are correct ways to
wait, such as using a WaitHandle sub-class, that don't consume CPU
resources uselessly as your code does.

If you can provide a concise-but-complete code sample, I'm sure I or
someone else can provide more useful advice. Until then, I remain
unoptimistic. :)

Pete
Sep 11 '08 #4
I wished I could provide an attachment, but I can't.

I added one ugly Application.DoEvents() to the wait loop, and my thread went
into the RunWorkerCompleted section.

I could email you the file, if you are interested. You can send your email
address to me at jpool at aaon dot com.
Sep 11 '08 #5
On Thu, 11 Sep 2008 14:17:03 -0700, jp2msft
<jp*****@discussions.microsoft.comwrote:
I wished I could provide an attachment, but I can't.
Sure you can. If you want the best answer, you must.

It doesn't have to be the full code you're working with. In fact, it
typically shouldn't be. But it does have to be complete, and it does have
to reproduce the problem.

Every question ever posted to this newsgroup has such as
"concise-but-complete" code sample that goes along with the question.
Often the person posting the question fails to include the sample, but one
always exists.
I added one ugly Application.DoEvents() to the wait loop, and my thread
went
into the RunWorkerCompleted section.
Don't do that. Calling DoEvents() is simply bad generally (the biggest
issue is that it introduces re-entrancy to code that has almost certainly
not been designed with re-entrancy in mind), and in this case it only
emphasizes how defective your current design is (you should never be
blocking the GUI thread, and I'll reiterate that having one thread just
sit and wait for another thread is always the wrong thing to do).
I could email you the file, if you are interested. You can send your
email
address to me at jpool at aaon dot com.
Any code sample I'd be willing to read through would be appropriate to be
posted here.

That said, based on your reply, it sounds like you've confirmed my guess
as to what's wrong. The correct resolution is to fix your design so that
the main GUI thread isn't waiting on the worker thread at all. Put all of
the logic that occurs after the wait into a method that can be called by
the RunWorkerCompleted event handler, or into that handler's method
itself. The main GUI thread should simply start the worker and then
return.

Pete
Sep 11 '08 #6
Ok, here is a small test section that I have put together to illustrate my
problem:

We have an SQL query that takes 60-120 seconds to run for each employee (the
SqlCommand's CommandTimeout is set to 120 seconds and this seems to give the
queries enough time).

public static string m_fmtSql = "SELECT LOTS OF STUFF WHERE EMPLOYEE='{0}'";
public static SqlConnection m_conn; // set elsewhere

void worker_DoWork(object sender, DoWorkEventArgs e) {
List<stringEmployees = (List<string>)e.Argument;
List<stringErrors = new List<string>();
List<stringResults = new List<string>();
BackgroundWorker worker = sender as BackgroundWorker;
int Count = 0;
foreach (string person in Employees) {
if (worker.CancellationPending == true) {
e.Cancel = true;
return;
}
Count++;
worker.ReportProgress(100 * Count / Employees.Count);
DataTable dt = new DataTable();
string sql = string.Format(m_fmtSql, person);
using (SqlDataAdapter da = new SqlDataAdapter(sql, m_conn)) {
try {
da.Fill(dt); // this step takes 60-120 seconds to run
} catch (SqlException err) {
Errors.Add(err.Message);
}
}
if (0 < dt.Rows.Count) {
string fmt = "{0};{1};{2};{3};{4};{5};{6}";
string var1, var2, var3, var4, var5;
int var6, var7;
// work with data, manipulate some figures, then add it to the results
string result
= string.Format(fmt, var1, var2, var3, var4, var5, var6, var7);
Results.Add(result);
}
}
e.Result = Results;
}

void worker_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e) {
if (e.Error != null) {
// display error
} else if (e.Cancelled == true) {
// display that status
} else if (e.Result is List<string>) {
List<stringResults = (List<string>)e.Result;
DataGridView1.Rows.Clear();
foreach (string line in Results) {
string[] data = line.Split(new char[] { ';' });
DataGridView1.Rows.Add(data);
}
}
}

void BtnCancel_Click(object sender, EventArgs e) {
if ((WorkerThread.IsBusy == true) &&
(WorkerThread.SupportsCancellation == true)) {
WorkerThread.CancelAsync();
// Here is the point of concern:
// since the SQL call could take up to 2 full minutes to complete,
// our Users are liable to try running the report again while
// the thread is still active.
// So, the solution is to disable the "Run Report" button until
// this thread has finished:
BtnRunReport.Enabled = false;
do {
Thread.Sleep(0);
} while (WorkerThread.IsBusy == true);
BtnRunReport.Enabled = true;
// The problem is:
// This code is so busy in the loop, that the
// "RunWorkerCompleted" code does not have a chance to run.
// If I write in the ugly "Application.DoEvents();" within the
do...while loop,
// the application will stop itself, but that is a bad fix.
}
}
Sep 12 '08 #7
On Fri, 12 Sep 2008 09:14:07 -0700, jp2msft
<jp*****@discussions.microsoft.comwrote:
Ok, here is a small test section that I have put together to illustrate
my
problem:
Please see http://www.yoda.arachsys.com/csharp/complete.html

Based on what you've written so far in this thread, I believe my previous
comments should be sufficient in helping you fix your code. But if you
feel there's a need for further elaboration, you need to post a true
concise-but-complete code sample. What you just posted is neither.
Sep 12 '08 #8

This discussion thread is closed

Replies have been disabled for this discussion.

Similar topics

1 post views Thread by David Veeneman | last post: by
reply views Thread by Smokey Grindel | last post: by

By using Bytes.com and it's services, you agree to our Privacy Policy and Terms of Use.

To disable or enable advertisements and analytics tracking please visit the manage ads & tracking page.