BackgroundWorker

BackgroundWorker – класс-обертка из пространства имен System.ComponentModel для управления рабочими потоками. Он обеспечивает следующие возможности:

§ Флаг отмены операции для завершения потока без использования Abort.

§ Стандартный протокол для сообщений о ходе выполнения операции, ее завершении и отмене.

§ Реализация интерфейса IComponent для размещения в дизайнере Visual Studio.

§ Обработка исключений в рабочем потоке.

§ Возможность обновления элементов управления Windows Forms в процессе выполнения или при завершении операции.

Последние две возможности особенно полезны – они означают, что можно не добавлять try/catch в рабочий метод и обновлять элементы управления без использования Control.Invoke.

BackgroundWorker использует пул многократно используемых потоков, чтобы не создавать их под каждую новую задачу. Это означает, что нельзя вызывать Abort для потока BackgroundWorker.

Вот минимально необходимые действия для использования BackgroundWorker:

§ Создайте экземпляр BackgroundWorker и назначьте обработчик для события DoWork.

§ Вызовите RunWorkerAsync,если необходимо – с нужным object в качестве аргумента.

После этих действий начнется исполнение. Аргумент, переданный в RunWorkerAsync, будет перенаправлен в обработчик DoWork как свойство Argument параметра DoWorkEventArgs. Вот пример:

class Program { static BackgroundWorker bw = new BackgroundWorker(); static void Main() { bw.DoWork += bw_DoWork; bw.RunWorkerAsync("Message to worker"); Console.ReadLine(); }   static void bw_DoWork(object sender, DoWorkEventArgs e) { // Это будет вызвано рабочим потоком Console.WriteLine(e.Argument); // напечатает "Message to worker" // Выполняем длительную операцию... }

BackgroundWorkerтакже предоставляет событие RunWorkerCompleted, которое генерируется после завершения трудов обработчика события DoWork. Обработка RunWorkerCompleted необязательна, но обычно применяется, хотя бы для того, чтобы получить информацию о возможных исключениях в DoWork.Кроме того, код в обработчике RunWorkerCompleted может обновлять элементы управления Windows Forms без явного маршалинга, а код в DoWork – нет.

Чтобы добавить отображение выполнения операции:

§ Установите свойство WorkerReportsProgress в true.

§ Периодически вызывайте ReportProgress из обработчика DoWork с указанием количества “выполненных процентов” и, возможно, с user-state объектом.

§ Обрабатывайте событие ProgressChanged, запрашивая свойство ProgressPercentage его аргумента.

Код в обработчике ProgressChanged может свободно обращаться к элементам управления UI так же, как и в RunWorkerCompleted. Обычно это нужно для обновления индикатора прогресса.

Чтобы иметь возможность отмены операции:

§ Установите свойство WorkerSupportsCancellation в true.

§ Периодически проверяйте свойство CancellationPending в обработчике DoWork – если оно установлено в true, установите свойство Cancel аргумента DoWorkEventArgs в true и сделайте return (можно устанавливать Cancel в true и без опроса CancellationPending – если выполнение задания не может быть продолжено).

§ Для запроса отмены операции вызывайте CancelAsync.

Вот пример, реализующий обе описанные возможности:

using System; using System.Threading; using System.ComponentModel;   class Program { static BackgroundWorker bw;   static void Main() { bw = new BackgroundWorker(); bw.WorkerReportsProgress = true; bw.WorkerSupportsCancellation = true; bw.DoWork += bw_DoWork; bw.ProgressChanged += bw_ProgressChanged; bw.RunWorkerCompleted += bw_RunWorkerCompleted;   bw.RunWorkerAsync(null);   Console.WriteLine( "Нажмите Enter в течении следующих пяти секунд, чтобы прервать работу"); Console.ReadLine();   if (bw.IsBusy) { bw.CancelAsync(); Console.ReadLine(); } }   static void bw_DoWork(object sender, DoWorkEventArgs e) { for (int i = 0; i <= 100; i += 20) { if (bw.CancellationPending) { e.Cancel = true; return; }   bw.ReportProgress(i); Thread.Sleep(1000); }   e.Result = 123; // будет передано в RunWorkerComрleted }   static void bw_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e) { if (e.Cancelled) Console.WriteLine( "Работа BackgroundWorker была прервана пользователем!"); else if (e.Error != null) Console.WriteLine("Worker exception: " + e.Error); else Console.WriteLine("Работа закончена успешно. Результат - " + e.Result + ". ");   Console.WriteLine("Нажмите Enter для выхода из программы..."); }   static void bw_ProgressChanged(object sender, ProgressChangedEventArgs e) { Console.WriteLine("Обработано " + e.ProgressPercentage + "%"); } }

Консольный вывод (Enter нажат во время работы BackgroundWorker):

Нажмите Enter в течении следующих пяти секунд, чтобы прервать работу... Обработано 0% Обработано 20% Обработано 40%   Работа BackgroundWorker была прервана пользователем! Нажмите Enter для выхода из программы...

Консольный вывод (работа BackgroundWorker не прерывалась):