Этот BackgroundWorker в настоящее время занят и не может выполнять несколько задач одновременно - PullRequest
0 голосов
/ 21 декабря 2011

Я пытаюсь использовать Background Worker в приложении WPF. Тяжелая задача использует WebClient для загрузки HTML-кода и анализа некоторой информации из него. В идеале я хочу делать это, загружая и анализируя, не блокируя пользовательский интерфейс и не помещая результаты в пользовательский интерфейс, как только он заработает.

И все работает нормально, однако, если я быстро отправлю команду «загрузить и проанализировать», я получу ошибку:

Этот BackgroundWorker в настоящее время занят и не может выполнять несколько задач одновременно

Итак, я немного прогуглил, и похоже, что я могу включить свойство .WorkerSupportsCancellation фонового рабочего и просто .CancelAsync(). Однако это не работает должным образом (отмена текущей загрузки и анализ).

Я все еще получаю вышеуказанную ошибку.

Вот мой код:

//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()!
}

Ответы [ 6 ]

6 голосов
/ 21 декабря 2011

Вызов CancelAsync все равно вызовет событие RunWorkerCompleted.В этом случае вам нужно убедиться, что CancelAsync не был вызван, проверив e.Cancelled.Пока это событие не сработает, вы не сможете позвонить RunWorkerAsync.

. В качестве альтернативы я бы порекомендовал вам делать то, что предложил Тигран, и каждый раз создавать новый BackgroundWorker.

Более того, я бы порекомендовалсохраняя результаты _backgroundWorker_DoWork в e.Result, затем извлекая их из того же самого в _backgroundWorker_RunWorkerCompleted

Может быть что-то вроде этого

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()!
}
5 голосов
/ 21 декабря 2011

Дело в том, что CancelAsync() делает то, что делает: отменяет асинхронно . Это означает, что он не остановится сразу , но через некоторое время. Это время никогда не может быть рассчитано или предсказано, поэтому у вас есть несколько вариантов:

  1. Подождите, пока эта backround worker остановится на самом деле , ожидая в цикле, пока IsBusy свойство не станет false

  2. Или, я думаю, лучшее решение - запустить другой background worker, учитывая, что запрос на отмену уже отправлен первому, поэтому он будет рано или поздно остановлен. В этом случае вам нужно знать из , какие background worker данные поступают, для того, чтобы обрабатывать их или нет, потому что при запуске второй все еще будет работать первая и выкачать данные из WebService.

Надеюсь, это поможет.

2 голосов
/ 21 декабря 2011

CancelAsync возвращается до того, как работник отменяет и останавливает свою работу.Следовательно, ваш вызов RunWorkerAsync начинается до того, как работник готов, и вы получаете эту ошибку.Сначала вам нужно будет дождаться, пока рабочий будет готов.

0 голосов
/ 21 декабря 2011

Вы вызываете CancelAsync, не дожидаясь, пока фоновый работник фактически отменит работу. Также у вас должна быть своя логика для отмены работы. На MSDN есть хороший пример, который показывает, как это сделать. В основном в вашем методе parseanddownloadresult () вам нужно проверить свойство CancellationPending .

0 голосов
/ 21 декабря 2011

Вы должны подтвердить, прежде чем вы пинаете.

f( !bw.IsBusy )
    bw.RunWorkerAsync();
else
    MessageBox.Show("Can't run the bw twice!");
0 голосов
/ 21 декабря 2011

Когда меня не интересует отслеживание хода асинхронной операции, я склоняюсь к тому, чтобы просто щелкнуть лямбду на ThreadPool.QueueUserWorkItem вместо того, чтобы создавать экземпляр фонового работника, в котором я должен проверить состояние, чтобы иметь возможность для повторного использования.

...