2014-01-05 2 views
0

Я пытаюсь (еще раз) реализовать Backgroundworker в своем приложении, чтобы пользовательский интерфейс был отзывчивым. Пользователь выбирает файлы для обработки, которые заканчиваются сеткой. Когда начинается обработка это так:BackgroundWorker вопросы об отмене

for (int i = 0; i<Grid.Rows.Count; i++) 
{ 

    Worker work = new Worker(); 
    //set up data for processing based on the current row in the grid 
    bw.RunWorkerAsync(work); 
    addStatusToGrid(); 
    //clean up; indicate work on this file is done 
    work=null; 


} 
void bw_DoWork(object sender, DoWorkEventArgs e) 
{ 
    Worker work = (Worker)e.Argument; 

    if (work.doTheWork() == false) 
    { 
     //indicate failure 

    } 
    else 
    { 
     //indicate success 
    } 
} 
private void bw_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e) 
{ 
    //not sure what to do here 
} 

НО, что происходит в том, что как только bw.RunWorkerAsync() называется приложение сразу же переходит к следующей строке в течение цикла, а затем начинается тот же процесс снова. Теперь я вроде хочу этого, но получаю сообщение об ошибке: «Этот рабочий стол в настоящее время занят и не может одновременно запускать несколько задач». Если я просто обрабатываю один элемент, addStatusToGrid() вызывается сразу, и, конечно, нет статуса для добавления, поскольку работа не выполняется.

НЕСКОЛЬКО ВОПРОСОВ: Если я могу использовать этот механизм для запуска сразу нескольких сеансов обработки, это будет хорошо. Но как я могу предотвратить немедленное вызов addStatusToGrid()?

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

+0

Сколько строк необходимо обрабатывать таким образом? – Steve

+1

Хорошо в ореховой скорлупе вам нужно обновить сетку в завершенном событии. т.е. начать фоновые задания. По завершении каждого результата отображаются результаты. –

+0

Я отредактировал ваш заголовок. Пожалуйста, смотрите: «Если вопросы включают« теги »в их названиях?] (Http://meta.stackexchange.com/questions/19190/), где консенсус« нет, они не должны ». –

ответ

0

С вашего вопроса выясняется, что у вас есть несколько строк в сетке, которые необходимо обработать. Теперь фоновый работник выполняет этот метод в другом потоке, освобождая пользовательский интерфейс для выполнения дополнительных действий. (Обновления статуса IE). Поскольку это выполняется в другом потоке, пользовательский интерфейс может продолжить обработку как обычно. Нить пользовательского интерфейса не будет ждать завершения BackgroundWorker. Если бы это было так, то использование рабочего фона было бы бесполезным. Кроме того, BackgroundWorker должен завершить операцию перед началом другой операции (таким образом, ваше сообщение об ошибке).

Теперь трудно понять, что вы делаете в work.doTheWork(), но я подозреваю, что вы хотели бы использовать другой подход здесь.

Прежде чем мы начнем решать вопрос, первое, что вы должны понять, это аннулирование фонового работника, фактически не отменившего поток, но предоставляющий «флаг» для вашего кода, чтобы проверить, ожидает ли BackgroundWorker ожидающего вычисления (MSDN on this flag) ,

Перемещение вперед. Поскольку Grid хранится в потоке пользовательского интерфейса, вам нужно понять данные из сетки, которые мы хотим захватить, и передать их в контексте BackgroundWorker. Поскольку этот inst предоставил, я собираюсь сделать очень простой пример. Теперь вы можете запускать несколько фоновых работников для обработки каждой строки отдельно, таким образом, создавая 1 работника в строке в Grid. Для вашего требования это может быть идеальным, но для больших сеток вы по существу создаете 1 поток на строку для обработки, а в сетке с сотнями строк это может быть катастрофическим.

Итак, вы можете создать метод, как показано ниже. Я прокомментировал код, чтобы помочь вам запустить его. Bascially это показывает возможность отменить работника, сообщить о прогрессе и выполнить итерацию каждого элемента строки отдельно. Я также добавил некоторые базовые классы для использования внутри рабочего. [Примечание Это демонстрационный код только]

class DoWorkTester 
{ 
    int currentIndex = 0; 
    GridRow[] rows; 


    public void ExecuteWorkers() 
    { 

     GridRow rowA = new GridRow 
     { 
      PropertyA = "abc", 
      PropertyB = "def" 
     }; 

     GridRow rowB = new GridRow 
     { 
      PropertyA = "123", 
      PropertyB = "456" 
     }; 

     GridRow rowC = new GridRow 
     { 
      PropertyA = "xyz", 
      PropertyB = "789" 
     }; 

     rows = new GridRow[] { rowA, rowB, rowC }; 

     currentIndex = 0; 

     runWorkers(); 
    } 

    BackgroundWorker worker; 

    void runWorkers() 
    { 
     //done all rows 
     if (currentIndex >= rows.Length - 1) 
      return; 

     //is the worker busy? 
     if (worker != null && worker.IsBusy) 
     { 
      //TODO: Trying to execute on a running worker. 
      return; 
     } 

     //create a new worker 
     worker = new BackgroundWorker(); 
     worker.WorkerSupportsCancellation = true; 
     worker.WorkerReportsProgress = true; 
     worker.ProgressChanged += (o, e) => 
     { 
      //TODO: Update the UI that the progress has changed 
     }; 
     worker.DoWork += (o, e) => 
     { 
      if (currentIndex >= rows.Length - 1) 
      { 
       //indicate done 
       e.Result = new WorkResult 
       { 
        Message = "", 
        Status = WorkerResultStatus.DONE 
       }; 
       return; 
      } 
      //check if the worker is cancelling 
      else if (worker.CancellationPending) 
      { 
       e.Result = WorkResult.Cancelled; 
       return; 
      } 
      currentIndex++; 

      //report the progress to the UI thread. 
      worker.ReportProgress(currentIndex); 

      //TODO: Execute your logic. 
      if (worker.CancellationPending) 
      { 
       e.Result = WorkResult.Cancelled; 
       return; 
      } 

      e.Result = WorkResult.Completed; 
     }; 
     worker.RunWorkerCompleted += (o, e) => 
     { 
      var result = e.Result as WorkResult; 
      if (result == null || result.Status != WorkerResultStatus.DONE) 
      { 
       //TODO: Code for cancelled \ failed results 
       worker.Dispose(); 
       worker = null; 
       return; 
      } 
      //Re-call the run workers thread 
      runWorkers(); 
     }; 
     worker.RunWorkerAsync(rows[currentIndex]); 
    } 

    /// <summary> 
    /// cancel the worker 
    /// </summary> 
    void cancelWorker() 
    { 
     //is the worker set to an instance and is it busy? 
     if (worker != null && worker.IsBusy) 
      worker.CancelAsync(); 

    } 
} 

enum WorkerResultStatus 
{ 
    DONE, 
    CANCELLED, 
    FAILED 
} 

class WorkResult 
{ 
    public string Message { get; set; } 
    public WorkerResultStatus Status { get; set; } 

    public static WorkResult Completed 
    { 
     get 
     { 
      return new WorkResult 
      { 
       Status = WorkerResultStatus.DONE, 
       Message = "" 
      }; 
     } 
    } 

    public static WorkResult Cancelled 
    { 
     get 
     { 
      return new WorkResult 
      { 
       Message = "Cancelled by user", 
       Status = WorkerResultStatus.CANCELLED 
      }; 
     } 
    } 
} 


class GridRow 
{ 
    public string PropertyA { get; set; } 
    public string PropertyB { get; set; } 
} 

Теперь, если вы хотите обработать несколько строк в то время вам придется адаптировать код, чтобы использовать какое-то работник Pooling или передать все данные строки на первый фон таким образом, удаляя рекурсию.

Cheers.

+0

Это потребует больше, чем небольшое изменение в логике. Да, я бы обрабатывал несколько строк. Я мог инкапсулировать данные строки как объекты и передать их массив; не будет слишком большим количеством ударов по памяти. Я должен был бы гарантировать, что если бы несколько потоков были порождены, они каким-то образом были бы ограничены, так как может быть 1000 строк. Вряд ли, но возможно. Нерест больше потоков, чем ядра, не имеет смысла, поскольку они просто будут ждать. – Ron

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