Класс BackgroundWorker может запустить отдельный поток, которому обычно поручают какую-нибудь фоновую продолжительную работу. Эта возможность позволяет сохранить работоспособность интерфейса пользователя (основной поток программы не блокируется), когда программа производит некоторые действия, требующие блокировки на ожидание какого-то события.
Цикл потока 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"); }
|