Alex Rivera | Logout

How to wait for a BackgroundWorker to cancel?

Asked 2008-09-23T20:30:50.430
130

Consider a hypothetical method of an object that does stuff for you:

public class DoesStuff
{
    BackgroundWorker _worker = new BackgroundWorker();

    ...

    public void CancelDoingStuff()
    {
        _worker.CancelAsync();

        //todo: Figure out a way to wait for BackgroundWorker to be cancelled.
    }
}

How can one wait for a BackgroundWorker to be done?


In the past people have tried:

while (_worker.IsBusy)
{
    Sleep(100);
}

But this deadlocks, because IsBusy is not cleared until after the RunWorkerCompleted event is handled, and that event can't get handled until the application goes idle. The application won't go idle until the worker is done. (Plus, it's a busy loop - disgusting.)

Others have add suggested kludging it into:

while (_worker.IsBusy)
{
    Application.DoEvents();
}

The problem with that is that is Application.DoEvents() causes messages currently in the queue to be processed, which cause re-entrancy problems (.NET isn't re-entrant).

I would hope to use some solution involving Event synchronization objects, where the code waits for an event - that the worker's RunWorkerCompleted event handlers sets. Something like:

Event _workerDoneEvent = new WaitHandle();

public void CancelDoingStuff()
{
    _worker.CancelAsync();
    _workerDoneEvent.WaitOne();
}

private void RunWorkerCompletedEventHandler(sender object, RunWorkerCompletedEventArgs e)
{
    _workerDoneEvent.SetEvent();
}

But I'm back to the deadlock: the event handler can't run until the application goes idle, and the application won't go idle because it's waiting for an Event.

So how can you wait for an BackgroundWorker to fin

Edit
Report

3 Answers

8

Almost all of you are confused by the question, and are not understanding how a worker is used.

Consider a RunWorkerComplete event handler:

private void OnRunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
{
    if (!e.Cancelled)
    {
        rocketOnPad = false;
        label1.Text = "Rocket launch complete.";
    }
    else
    {
        rocketOnPad = true;
        label1.Text = "Rocket launch aborted.";
    }
    worker = null;
}

And all is good.

Now comes a situation where the caller needs to abort the countdown because they need to execute an emergency self-destruct of the rocket.

private void BlowUpRocket()
{
    if (worker != null)
    {
        worker.CancelAsync();
        WaitForWorkerToFinish(worker);
        worker = null;
    }

    StartClaxon();
    SelfDestruct();
}

And there is also a situation where we need to open the access gates to the rocket, but not while doing a countdown:

private void OpenAccessGates()
{
    if (worker != null)
    {
        worker.CancelAsync();
        WaitForWorkerToFinish(worker);
        worker = null;
    }

    if (!rocketOnPad)
        DisengageAllGateLatches();
}

And finally, we need to de-fuel the rocket, but that's not allowed during a countdown:

private void DrainRocket()
{
    if (worker != null)
    {
        worker.CancelAsync();
        WaitForWorkerToFinish(worker);
        worker = null;
    }

    if (rocketOnPad)
        OpenFuelValves();
}

Without the ability to wait for a worker to cancel, we must move all three methods to the RunWorkerCompletedEvent:

private void OnRunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
{
    if (!e.Cancelled)
    {
        rocketOnPad = false;
        label1.Text = "Rocket launch complete.";
    }
    else
    {
        rocketOnPad = true;
        label1.Text = "Rocket launch 
answered 2008-09-24T14:09:27.720
1

I don't understand why you'd want to wait for a BackgroundWorker to complete; it really seems like the exact opposite of the motivation for the class.

However, you could start every method with a call to worker.IsBusy and have them exit if it is running.

answered 2008-09-23T20:41:31.577
-2

oh man, some of these have gotten ridiculously complex. all you need to do is check the BackgroundWorker.CancellationPending property inside the DoWork handler. you can check it at any time. once it's pending, set e.Cancel = True and bail from the method.

// method here private void Worker_DoWork(object sender, DoWorkEventArgs e) { BackgroundWorker bw = (sender as BackgroundWorker);

// do stuff

if(bw.CancellationPending)
{
    e.Cancel = True;
    return;
}

// do other stuff

}

answered 2008-09-24T19:57:11.160

Your Answer