Поделиться через


Пошаговое руководство. Реализация формы, в которой выполняется фоновая операция

Обновлен: Ноябрь 2007

Если какая-либо операция будет выполняться в течение долгого времени, и при этом требуется не допустить, чтобы пользовательский интерфейс перестал отвечать на запросы пользователя или "завис", можно использовать класс BackgroundWorker для выполнения операции в другом потоке.

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

В этом пошаговом руководстве, в частности, рассматриваются следующие задачи:

  • Создание приложения Windows

  • Создание BackgroundWorker в форме

  • Добавление асинхронных обработчиков событий

  • Добавление отчета о ходе выполнения и поддержки отмены

Полный текст кода для текущего примера см. в разделе Практическое руководство. Реализация формы, в которой выполняется фоновая операция.

b2zk6580.alert_note(ru-ru,VS.90).gifПримечание.

Отображаемые диалоговые окна и команды меню могут отличаться от описанных в справке в зависимости от текущих настроек или выпуска среды. Для изменения настроек выберите Параметры импорта и экспорта в меню Сервис. Дополнительные сведения см. в разделе Параметры Visual Studio.

Создание проекта

Для начала следует создать проект и подготовить форму.

Чтобы создать форму, использующую фоновую операцию

  1. Создайте проект приложения Windows под названием BackgroundWorkerExample. Дополнительные сведения см. в разделе Практическое руководство. Создание проекта приложения Windows.

  2. В Обозревателе решений щелкните правой кнопкой мыши Form1 и выберите в контекстном меню Переименовать. Измените имя файла на FibonacciCalculator. Нажмите кнопку Да, чтобы переименовать все ссылки на элемент кода Form1.

  3. Перетащите элемент управления NumericUpDown из панели элементов в форму. Установите свойство Minimum равным 1, а свойство Maximum равным 91.

  4. Добавьте в форму два элемента управления Button.

  5. Переименуйте первый элемент управления ButtonstartAsyncButton и установите свойство Text равным Start Async. Переименуйте второй элемент управления ButtoncancelAsyncButton и установите свойство Text равным Cancel Async. Установите для свойства Enabled значение false.

  6. Создайте обработчик событий для событий Click обоих элементов управления Button. Дополнительные сведения см. в разделе Руководство: создание обработчика событий с помощью конструктора.

  7. Перетащите элемент управления Label из панели элементов в форму и переименуйте его в resultLabel.

  8. Перетащите элемент управления ProgressBar из панели элементов в форму.

Создание BackgroundWorker в форме

Можно создать BackgroundWorker для асинхронной операции с помощью конструктораWindows.

Чтобы создать BackgroundWorker с помощью конструктора

  • Со вкладки Компонентыпанели элементов перетащите BackgroundWorker.

Добавление асинхронных обработчиков событий

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

Реализация асинхронных обработчиков событий

  1. В окне Свойства нажмите кнопку События. Дважды щелкните события DoWorkи RunWorkerCompleted, чтобы создать обработчики событий. Дополнительные сведения об использовании обработчиков событий см. в разделе Руководство: создание обработчика событий с помощью конструктора.

  2. Создайте в форме в новый метод с именем ComputeFibonacci. Этот метод будет выполнять вычисления, он будет запущен в фоновом режиме. Код демонстрирует рекурсивную реализацию алгоритма Фибоначчи — она весьма неэффективна, вычисление больших чисел Фибоначчи с ее помощью занимает немало времени. Она используется здесь для иллюстрации: чтобы показать, как выполнение операции может вызвать значительные задержки в работе приложения.

    ' This is the method that does the actual work. For this
    ' example, it computes a Fibonacci number and
    ' reports progress as it does its work.
    Function ComputeFibonacci( _
        ByVal n As Integer, _
        ByVal worker As BackgroundWorker, _
        ByVal e As DoWorkEventArgs) As Long
    
        ' The parameter n must be >= 0 and <= 91.
        ' Fib(n), with n > 91, overflows a long.
        If n < 0 OrElse n > 91 Then
            Throw New ArgumentException( _
                "value must be >= 0 and <= 91", "n")
        End If
    
        Dim result As Long = 0
    
        ' Abort the operation if the user has canceled.
        ' Note that a call to CancelAsync may have set 
        ' CancellationPending to true just after the
        ' last invocation of this method exits, so this 
        ' code will not have the opportunity to set the 
        ' DoWorkEventArgs.Cancel flag to true. This means
        ' that RunWorkerCompletedEventArgs.Cancelled will
        ' not be set to true in your RunWorkerCompleted
        ' event handler. This is a race condition.
        If worker.CancellationPending Then
            e.Cancel = True
        Else
            If n < 2 Then
                result = 1
            Else
                result = ComputeFibonacci(n - 1, worker, e) + _
                         ComputeFibonacci(n - 2, worker, e)
            End If
    
            ' Report progress as a percentage of the total task.
            Dim percentComplete As Integer = _
                CSng(n) / CSng(numberToCompute) * 100
            If percentComplete > highestPercentageReached Then
                highestPercentageReached = percentComplete
                worker.ReportProgress(percentComplete)
            End If
    
        End If
    
        Return result
    
    End Function
    
    // This is the method that does the actual work. For this
    // example, it computes a Fibonacci number and
    // reports progress as it does its work.
    long ComputeFibonacci(int n, BackgroundWorker worker, DoWorkEventArgs e)
    {
        // The parameter n must be >= 0 and <= 91.
        // Fib(n), with n > 91, overflows a long.
        if ((n < 0) || (n > 91))
        {
            throw new ArgumentException(
                "value must be >= 0 and <= 91", "n");
        }
    
        long result = 0;
    
        // Abort the operation if the user has canceled.
        // Note that a call to CancelAsync may have set 
        // CancellationPending to true just after the
        // last invocation of this method exits, so this 
        // code will not have the opportunity to set the 
        // DoWorkEventArgs.Cancel flag to true. This means
        // that RunWorkerCompletedEventArgs.Cancelled will
        // not be set to true in your RunWorkerCompleted
        // event handler. This is a race condition.
    
        if (worker.CancellationPending)
        {   
            e.Cancel = true;
        }
        else
        {   
            if (n < 2)
            {   
                result = 1;
            }
            else
            {   
                result = ComputeFibonacci(n - 1, worker, e) + 
                         ComputeFibonacci(n - 2, worker, e);
            }
    
            // Report progress as a percentage of the total task.
            int percentComplete = 
                (int)((float)n / (float)numberToCompute * 100);
            if (percentComplete > highestPercentageReached)
            {
                highestPercentageReached = percentComplete;
                worker.ReportProgress(percentComplete);
            }
        }
    
        return result;
    }
    
    // This is the method that does the actual work. For this
    // example, it computes a Fibonacci number and
    // reports progress as it does its work.
    long ComputeFibonacci( int n, BackgroundWorker^ worker, DoWorkEventArgs ^ e )
    {
       // The parameter n must be >= 0 and <= 91.
       // Fib(n), with n > 91, overflows a long.
       if ( (n < 0) || (n > 91) )
       {
          throw gcnew ArgumentException( "value must be >= 0 and <= 91","n" );
       }
    
       long result = 0;
    
       // Abort the operation if the user has cancelled.
       // Note that a call to CancelAsync may have set 
       // CancellationPending to true just after the
       // last invocation of this method exits, so this 
       // code will not have the opportunity to set the 
       // DoWorkEventArgs.Cancel flag to true. This means
       // that RunWorkerCompletedEventArgs.Cancelled will
       // not be set to true in your RunWorkerCompleted
       // event handler. This is a race condition.
       if ( worker->CancellationPending )
       {
          e->Cancel = true;
       }
       else
       {
          if ( n < 2 )
          {
             result = 1;
          }
          else
          {
             result = ComputeFibonacci( n - 1, worker, e ) + ComputeFibonacci( n - 2, worker, e );
          }
    
          // Report progress as a percentage of the total task.
          int percentComplete = (int)((float)n / (float)numberToCompute * 100);
          if ( percentComplete > highestPercentageReached )
          {
             highestPercentageReached = percentComplete;
             worker->ReportProgress( percentComplete );
          }
       }
    
       return result;
    }
    
    // This is the method that does the actual work. For this
    // example, it computes a Fibonacci number and
    // reports progress as it does its work.
    long ComputeFibonacci(int n, BackgroundWorker worker, DoWorkEventArgs e)
    {
        // The parameter n must be >= 0 and <= 91.
        // Fib(n), with n > 91, overflows a long.
        if (n < 0 || n > 91) {
            throw new ArgumentException("value must be >= 0 and <= 91", "n");
        }
    
        long result = 0;
    
        // Abort the operation if the user has cancelled.
        // Note that a call to CancelAsync may have set 
        // CancellationPending to true just after the
        // last invocation of this method exits, so this 
        // code will not have the opportunity to set the 
        // DoWorkEventArgs.Cancel flag to true. This means
        // that RunWorkerCompletedEventArgs.Cancelled will
        // not be set to true in your RunWorkerCompleted
        // event handler. This is a race condition.
        if (worker.get_CancellationPending()) {
    
            e.set_Cancel(true);
        }
        else {
    
            if (n < 2) {
    
                result = 1;
            }
            else {
    
                result = ComputeFibonacci(n - 1, worker, e) 
                    + ComputeFibonacci(n - 2, worker, e);
            }
    
            // Report progress as a percentage of the total task.
            int percentComplete=(int)((float)(n)/(float)(numberToCompute)* 100);
    
            if (percentComplete > highestPercentageReached) {            
                highestPercentageReached = percentComplete;
                worker.ReportProgress(percentComplete);
            }
        }
    
        return result;
    } 
    
  3. В обработчике событий DoWork добавьте вызов метода ComputeFibonacci. Возьмите первый параметр ComputeFibonacci из свойства ArgumentDoWorkEventArgs. Параметры BackgroundWorker и DoWorkEventArgs будут использованы позднее для поддержки отчета о ходе выполнения и отмены.

b2zk6580.alert_note(ru-ru,VS.90).gifПримечание.

Обратите внимание, что обработчик DoWork не ссылается на переменную экземпляра backgroundWorker1 напрямую, поскольку в этом случае обработчик будет связан с определенным экземпляром BackgroundWorker. Вместо этого ссылка на BackgroundWorker, создавший событие, берется из параметра sender. Это важно, если в форме размещено несколько BackgroundWorker.

Присвойте значение, возвращенное из ComputeFibonacci, свойству ResultDoWorkEventArgs. Результат будет доступен для обработчика событий RunWorkerCompleted.

Добавление отчета о ходе выполнения и поддержки отмены

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

Для внедрения отчета о ходе выполнения

  1. В окне Свойства выберите backgroundWorker1. Установите свойствам WorkerReportsProgress и WorkerSupportsCancellation значение true.

  2. Объявите две переменные в форме FibonacciCalculator. Они будут использоваться для отслеживания хода выполнения.

    Private numberToCompute As Integer = 0
    Private highestPercentageReached As Integer = 0
    
    private int numberToCompute = 0;
    private int highestPercentageReached = 0;
    
    int numberToCompute;
    int highestPercentageReached;
    
    private int numberToCompute = 0;
    private int highestPercentageReached = 0;
    
  3. Добавьте обработчик события для события ProgressChanged. В обработчике события ProgressChanged обновите ProgressBar со свойством ProgressPercentage параметра ProgressChangedEventArgs.

    ' This event handler updates the progress bar.
    Private Sub backgroundWorker1_ProgressChanged( _
    ByVal sender As Object, ByVal e As ProgressChangedEventArgs) _
    Handles backgroundWorker1.ProgressChanged
    
        Me.progressBar1.Value = e.ProgressPercentage
    
    End Sub
    
    // This event handler updates the progress bar.
    private void backgroundWorker1_ProgressChanged(object sender,
        ProgressChangedEventArgs e)
    {
        this.progressBar1.Value = e.ProgressPercentage;
    }
    
    // This event handler updates the progress bar.
    void backgroundWorker1_ProgressChanged( Object^ /*sender*/, ProgressChangedEventArgs^ e )
    {
       this->progressBar1->Value = e->ProgressPercentage;
    }
    
    // This event handler updates the progress bar.
    private void backgroundWorker1_ProgressChanged(Object sender,
        ProgressChangedEventArgs e)
    {
        this.progressBar1.set_Value(e.get_ProgressPercentage());
    } //backgroundWorker1_ProgressChanged
    

Для реализации поддержки отмены

  1. В обработчике события Click элемента управления cancelAsyncButton добавьте код, отменяющий асинхронную операцию.

    Private Sub cancelAsyncButton_Click( _
    ByVal sender As System.Object, _
    ByVal e As System.EventArgs) _
    Handles cancelAsyncButton.Click
    
        ' Cancel the asynchronous operation.
        Me.backgroundWorker1.CancelAsync()
    
        ' Disable the Cancel button.
        cancelAsyncButton.Enabled = False
    
    End Sub 'cancelAsyncButton_Click
    
    private void cancelAsyncButton_Click(System.Object sender, 
        System.EventArgs e)
    {   
        // Cancel the asynchronous operation.
        this.backgroundWorker1.CancelAsync();
    
        // Disable the Cancel button.
        cancelAsyncButton.Enabled = false;
    }
    
    void cancelAsyncButton_Click( System::Object^ /*sender*/, System::EventArgs^ /*e*/ )
    {  
       // Cancel the asynchronous operation.
       this->backgroundWorker1->CancelAsync();
    
       // Disable the Cancel button.
       cancelAsyncButton->Enabled = false;
    }
    
    private void cancelAsyncButton_Click(Object sender, System.EventArgs e)
    {   
        // Cancel the asynchronous operation.
        this.backgroundWorker1.CancelAsync();
    
        // Disable the Cancel button.
        cancelAsyncButton.set_Enabled(false);
    }
    
  2. Следующие фрагменты кода в методе ComputeFibonacci сообщают о ходе выполнения и поддерживают отмену.

    If worker.CancellationPending Then
        e.Cancel = True
    
    
    ...
    
    
    ' Report progress as a percentage of the total task.
    Dim percentComplete As Integer = _
        CSng(n) / CSng(numberToCompute) * 100
    If percentComplete > highestPercentageReached Then
        highestPercentageReached = percentComplete
        worker.ReportProgress(percentComplete)
    End If
    
    if (worker.CancellationPending)
    {   
        e.Cancel = true;
    }
    
    
    ...
    
    
    // Report progress as a percentage of the total task.
    int percentComplete = 
        (int)((float)n / (float)numberToCompute * 100);
    if (percentComplete > highestPercentageReached)
    {
        highestPercentageReached = percentComplete;
        worker.ReportProgress(percentComplete);
    }
    
    if ( worker->CancellationPending )
    {
       e->Cancel = true;
    }
    
    
    ...
    
    
    // Report progress as a percentage of the total task.
    int percentComplete = (int)((float)n / (float)numberToCompute * 100);
    if ( percentComplete > highestPercentageReached )
    {
       highestPercentageReached = percentComplete;
       worker->ReportProgress( percentComplete );
    }
    
    if (worker.get_CancellationPending()) {
    
        e.set_Cancel(true);
    }
    
    
    ...
    
    
    // Report progress as a percentage of the total task.
    int percentComplete=(int)((float)(n)/(float)(numberToCompute)* 100);
    
    if (percentComplete > highestPercentageReached) {            
        highestPercentageReached = percentComplete;
        worker.ReportProgress(percentComplete);
    }
    

Контрольная точка

Теперь можно скомпилировать и запустить приложение "Fibonacci Calculator".

Чтобы проверить проект

  • Нажмите клавишу F5, чтобы скомпилировать и запустить приложение.

    При выполнении вычислений в фоновом режиме вы увидите ProgressBar с отображением хода выполнения вычислений. Также можно отменить выполняющуюся операцию.

    Для небольших чисел вычисление будет выполняться очень быстро, но при вычислении больших чисел возникнут задержки. При вводе значения 20 или более задержка может составить несколько секунд (в зависимости от мощности компьютера). Для значений, превышающих 40, вычисление может занять от нескольких минут до нескольких часов. Обратите внимание, что пока калькулятор вычисляет большой число Фибоначчи, можно свободно перемещать форму, сворачивать ее, разворачивать и даже закрывать. Это возможно из-за того, что основной поток пользовательского интерфейса не дожидается завершения вычисления.

Следующие действия

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

См. также

Задачи

Практическое руководство. Реализация формы, в которой выполняется фоновая операция

Пример. Фоновое выполнение операции

Основные понятия

Рекомендации по работе с потоками

Ссылки

BackgroundWorker

Другие ресурсы

Многопоточность в компонентах

Многопотоковость в Visual Basic

Компонент BackgroundWorker