Visual Studio C++ 2010 Express: BackgroundWorker Class Печать
Добавил(а) microsin   

Класс BackgroundWorker может запустить отдельный поток, которому обычно поручают какую-нибудь фоновую продолжительную работу. Эта возможность позволяет сохранить работоспособность интерфейса пользователя (основной поток программы не блокируется), когда программа производит некоторые действия, требующие блокировки на ожидание какого-то события.

BackgroundWorker-class.PNG

Цикл потока BackgroundWorker запускается в обработчике события DoWork, (OnDoWork). Внимание: из этого обработчика нельзя менять пользовательский интерфейс, иначе возникнет конфликт с основным потоком программы! Общение с пользовательским интерфейсом внутри потока DoWork возможно только через вызовы метода ReportProgress, что генерирует события ProgressChanged, которые можно обработать в потоке класса формы (свойство WorkerReportsProgress должно быть установлено при этом в true). Как начать работать с BackgroundWorker:

1. Бросаем на форму компонент BackgroundWorker (BackgroundWorker1).

2. В Form1_Load инициализируем BackgroundWorker:

//разрешение срабатывания событий ProgressChanged
BackgroundWorker1->WorkerReportsProgress = true;
//запуск тела DoWork
BackgroundWorker1->RunWorkerAsync();

3. Заходим в события BackgroundWorker1, и добавляем обработчики DoWork, ProgressChanged, RunWorkerCompleted.

4. Пишем в теле DoWork алгоритм работы BackgroundWorker наподобие следующего:

//////////////////////////////////////////////////////////////////////////////////
// Построение списка имеющихся портов
private: System::Void BackgroundWorker1_DoWork
                      (System::Object^ sender,
                       System::ComponentModel::DoWorkEventArgs^ e)
{
  BackgroundWorker^ worker = dynamic_cast<backgroundworker^>(sender);
  applog->Write("backgroundWorker DoWork");
  for(int i=1;i<100;++i)
  {
     sprintf_s(h411->CommName, COMM_NAME_LEN, "COM%d", i);
     if(h411->comport->Open(i,9600))
         worker->ReportProgress(i);
     h411->comport->Close();
  }
}

Здесь вызовы ReportProgress будут генерить события прогресса ProgressChanged.

5. Пишем тело обработчика события ProgressChanged наподобие такого:

//////////////////////////////////////////////////////////////////////////////////
// Обработчик события изменения статуса поиска портов
private: System::Void BackgroundWorker1_Changed
               (System::Object^ sender,
                System::ComponentModel::ProgressChangedEventArgs^ e)
{
  toolStripProgressBar1->Value = e->ProgressPercentage;
}

Здесь e->ProgressPercentage получает значение переменной i, через которую вызовом ReportProgress было передано значение прогресса.

6. Можно останавливать BackgroundWorker, если вызвать его метод CancelAsync():

BackgroundWorker1->CancelAsync();

Чтобы остановка сработала, нужно чтобы свойство WorkerSupportsCancellation было установлено в true. Кроме того, в теле цикла DoWork экземпляра BackgroundWorker нужно организовать проверку свойства CancellationPending, и если оно true, то прерывать выполнение цикла. Пример:

private: System::Void BackgroundWorker1_DoWork
                     (System::Object^ sender,
                      System::ComponentModel::DoWorkEventArgs^ e)
{
  BackgroundWorker^ worker = dynamic_cast<backgroundworker^>(sender);
  applog->Write("bBackgroundWorker1_DoWork START");
  for(idxOper=0; idxOper < dataGridView1->RowCount; idxOper++)
  {
     if (worker->CancellationPending)
         break;
     worker->ReportProgress(idxOper*100 / dataGridView1->RowCount);
     Sleep(200);
  }
  applog->Write("BackgroundWorker1_DoWork EXIT");
}

Примечание: имейте в виду, что при наличии длительно выполняющегося цикла в потоке BackgroundWorker необходимо освобождать процессорное время для основной формы (с помощью вызова Sleep), иначе приложение "заморозится" и не будет отвечать на интерфейс пользователя, пока цикл BackgroundWorker не завершится.

7. При завершении работы BackgroundWorker будет автоматически срабатывать событие RunWorkerCompleted. Оно срабатывает и при вызове CancelAsync(), если обработчик DoWork был завершен. Пример обработчика события завершения:

private: System::Void BackgroundWorker1_RunWorkerCompleted
                    (System::Object^ sender,
                     System::ComponentModel::RunWorkerCompletedEventArgs^ e)
{
  applog->Write("bwIterateCalibr_RunWorkerCompleted");
}