2011-12-21 1 views
0

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

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

This BackgroundWorker is currently busy and cannot run multiple tasks concurrently

Так что я сделал некоторые Googling, и кажется, что я могу позволить .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

Вызов 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()! 
} 
0

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

2

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

0

Вам нужно проверить перед вами пинки в

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

Это будет вызывать длинный каскад модальных MessageBox, от которых пользователь не может убежать. – djdanlib

+0

Kumar, ваш код был полезен для меня, поэтому я буду поддерживать – dovla091

5

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

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

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

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

0

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

Смежные вопросы