Only Bolivian Here
Only Bolivian Here

Reputation: 36753

This BackgroundWorker is currently busy and cannot run multiple tasks concurrently

I'm trying to use a Background Worker in a WPF application. The heavy lifting task uses WebClient to download some HTML and parse some info out of it. Ideally I want to do that downloading and parsing without locking the UI and placing the results in the UI once it's done working.

And it works fine, however, if I quickly submit the "download and parse" command, I get the error:

This BackgroundWorker is currently busy and cannot run multiple tasks concurrently

So I did some Googling and it seems that I can enable the .WorkerSupportsCancellation property of the background worker and just .CancelAsync(). However, this doesn't work as expected (canceling the current download and parse).

I still get the above error.

Here's my code:

//In window constructor.
_backgroundWorker.WorkerSupportsCancellation = true;
_backgroundWorker.DoWork += new DoWorkEventHandler(_backgroundWorker_DoWork);
_backgroundWorker.RunWorkerCompleted += new RunWorkerCompletedEventHandler(_backgroundWorker_RunWorkerCompleted);

//Declared at class level variable.
BackgroundWorker _backgroundWorker = new BackgroundWorker();

//This is the method I call from my UI.
private void LoadHtmlAndParse(string foobar)
{
    //Cancel whatever it is you're doing!
    _backgroundWorker.CancelAsync();

    //And start doing this immediately!
    _backgroundWorker.RunWorkerAsync(foobar);
}

POCOClassFoo foo = new POCOClassFoo();

void _backgroundWorker_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
{
    //This automagically sets the UI to the data.
    Foo.DataContext = foo;
}

void _backgroundWorker_DoWork(object sender, DoWorkEventArgs e)
{
    //DOING THE HEAVY LIFTING HERE!
    foo = parseanddownloadresult()!
}

Upvotes: 0

Views: 16712

Answers (6)

cadrell0
cadrell0

Reputation: 17327

Calling CancelAsync will still fire the RunWorkerCompleted event. In this event, you need to make sure that CancelAsync has not been called, by checking e.Cancelled. Until this event fires, you cannot call RunWorkerAsync.

Alternatively, I would recommend you do what Tigran suggested and create a new BackgroundWorker each time.

Further more, I would recommend storing the results of_backgroundWorker_DoWork in e.Result, then retrieve them from the same in _backgroundWorker_RunWorkerCompleted

Maybe something like this

BackgroundWorker _backgroundWorker;

private BackgroundWorker CreateBackgroundWorker()
{
    var bw = new BackgroundWorker();
    bw.WorkerSupportsCancellation = true;
    bw.DoWork += _backgroundWorker_DoWork;
    bw.RunWorkerCompleted += new  _backgroundWorker_RunWorkerCompleted;
    return bw.
}

private void LoadHtmlAndParse(string foobar)
{
    //Cancel whatever it is you're doing!
    if (_backgroundWorer != null)
    {
        _backgroundWorker.CancelAsync();
    }

    _backgroundWorker = CreateBackgroundWorker();

    //And start doing this immediately!
    _backgroundWorker.RunWorkerAsync(foobar);
}

//you no longer need this because the value is being stored in e.Result
//POCOClassFoo foo = new POCOClassFoo();

private void _backgroundWorker_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
{
    if (e.Error != null)
    {
        //Error handling goes here.
    }
    else
    {
        if (e.Cancelled)
        {
            //handle cancels here.
        }
        {
            //This automagically sets the UI to the data.
            Foo.DataContext = (POCOClassFoo)e.Result;
        }
}

private void _backgroundWorker_DoWork(object sender, DoWorkEventArgs e)
{
    //DOING THE HEAVY LIFTING HERE!
    e.Result = parseanddownloadresult()!
}

Upvotes: 6

Tigran
Tigran

Reputation: 62276

The thing is that CancelAsync() does what it climes: cancel in async way. That means that it will not stop immediately, but after some time. That time can never be calculated or predicted, so you have a couple of options:

  1. Wait until this backround worker stops really, by waiting in cycle until IsBusy property of it becomes false

  2. Or, I think, better solution is to start another background worker, considering that request of cancelation was already sent to the first one, so it will be soon or later stop. In this case, you need to know from which background worker data comes, in order to process it or not, cause on start of second the first one will still run and pump the data from WebService.

Hope this helps.

Upvotes: 5

Toni Parviainen
Toni Parviainen

Reputation: 2387

You are calling CancelAsync without waiting for the background worker to actually cancel the work. Also you must have your own logic for cancelling the work. There is a good example on MSDN which shows how to do it. Basically in your parseanddownloadresult() method you need to check the CancellationPending property.

Upvotes: 0

Kumar
Kumar

Reputation: 1015

You need to verify before you kicks in.

f( !bw.IsBusy )
    bw.RunWorkerAsync();
else
    MessageBox.Show("Can't run the bw twice!");

Upvotes: 0

djdanlib
djdanlib

Reputation: 22516

CancelAsync returns before the worker cancels and stops its work. Hence, your RunWorkerAsync call is starting before the worker is ready, and you're getting that error. You'll need to wait for the worker to be ready first.

Upvotes: 2

Johann Gerell
Johann Gerell

Reputation: 25591

When I'm not interested in tracking progress of an async operation, I tend to prefer to just slap a lambda at ThreadPool.QueueUserWorkItem instead of instantiating and setting up a background worker that I have to check the state of to be able to reuse in a sane way.

Upvotes: 0

Related Questions