Freigeben über


Exemplarische Vorgehensweise: Implementieren eines Formulars, das einen Hintergrundvorgang verwendet

Wenn Sie einen Vorgang haben, der lange dauern wird, und Sie nicht möchten, dass Die Benutzeroberfläche (UI) nicht mehr reagiert oder blockiert, können Sie die BackgroundWorker Klasse verwenden, um den Vorgang in einem anderen Thread auszuführen.

In dieser exemplarischen Vorgehensweise wird veranschaulicht, wie Sie die BackgroundWorker Klasse verwenden, um zeitaufwendige Berechnungen im Hintergrund durchzuführen, während die Benutzeroberfläche reaktionsfähig bleibt. Wenn Sie durch sind, verfügen Sie über eine Anwendung, die Fibonacci-Zahlen asynchron berechnet. Obwohl das Berechnen einer großen Fibonacci-Zahl eine spürbare Zeit in Anspruch nehmen kann, wird der Haupt-UI-Thread nicht durch diese Verzögerung unterbrochen, und das Formular wird während der Berechnung reaktionsfähig.

In dieser exemplarischen Vorgehensweise werden u. a. folgende Aufgaben veranschaulicht:

  • Erstellen einer Windows-basierten Anwendung

  • Erstellen eines BackgroundWorker in Ihrem Formular

  • Asynchrone Ereignishandler hinzufügen

  • Hinzufügen von Statusberichten und Support für Abbruch

Eine vollständige Auflistung des codes, der in diesem Beispiel verwendet wird, finden Sie unter How to: Implement a Form That Uses a Background Operation.

Erstellen eines Formulars, das einen Hintergrundvorgang verwendet

  1. Erstellen Sie in Visual Studio ein Windows-basiertes Anwendungsprojekt namens BackgroundWorkerExample "File>New>Project>Visual C# " oder "Visual Basic>Classic Desktop>Windows Forms Application".

  2. Klicken Sie im Projektmappen-Explorer mit der rechten Maustaste auf Form1 und wählen Sie im Kontextmenü "Umbenennen" aus. Ändern Sie den Dateinamen in FibonacciCalculator. Klicken Sie auf die Schaltfläche 'Ja ', wenn Sie gefragt werden, ob Sie alle Verweise auf das CodeelementForm1 '' umbenennen möchten.

  3. Ziehen Sie ein NumericUpDown Steuerelement aus der Toolbox auf das Formular. Legen Sie die Minimum-Eigenschaft auf 1 und die Maximum-Eigenschaft auf 91 fest.

  4. Fügen Sie dem Formular zwei Button-Steuerelemente hinzu.

  5. Benennen Sie das erste Button Steuerelement startAsyncButton um, und legen Sie die Text Eigenschaft auf Start Async. Benennen Sie das zweite Button Steuerelement cancelAsyncButtonum, und legen Sie die Text Eigenschaft auf Cancel Async. Legen Sie die zugehörige Enabled-Eigenschaft auf false fest.

  6. Erstellen Sie einen Ereignishandler für beide Ereignisse des Button Steuerelements Click . Ausführliche Informationen finden Sie unter Wie man: Ereignishandler mit dem Designer erstellt.

  7. Ziehen Sie ein Label Steuerelement aus der Toolbox auf das Formular, und benennen Sie es resultLabelum.

  8. Ziehen Sie ein ProgressBar Steuerelement aus der Toolbox auf das Formular.

Erstellen Sie einen BackgroundWorker mit dem Designer

Sie können das BackgroundWorker für Ihren asynchronen Vorgang mithilfe des WindowsForms-Designers erstellen.

Ziehen Sie auf der Registerkarte Komponenten der Toolbox ein BackgroundWorker auf das Formular.

Hinzufügen asynchroner Ereignishandler

Jetzt können Sie Ereignishandler für die asynchronen Ereignisse der BackgroundWorker Komponente hinzufügen. Der zeitaufwendige Vorgang, der im Hintergrund ausgeführt wird, der Fibonacci-Zahlen berechnet, wird von einem dieser Ereignishandler aufgerufen.

  1. Klicken Sie im Eigenschaftenfenster , wobei die BackgroundWorker Komponente noch ausgewählt ist, auf die Schaltfläche "Ereignisse ". Doppelklicken Sie auf die DoWork-Ereignisse und RunWorkerCompleted-Ereignisse, um Ereignishandler zu erstellen. Weitere Informationen zur Verwendung von Ereignishandlern finden Sie unter How to: Create Event Handlers Using the Designer.

  2. Erstellen Sie eine neue Methode namens ComputeFibonacci in Ihrem Formular. Diese Methode erledigt die eigentliche Arbeit und wird im Hintergrund ausgeführt. Dieser Code veranschaulicht die rekursive Implementierung des Fibonacci-Algorithmus, die besonders ineffizient ist und exponentiell mehr Zeit in Anspruch nimmt, um für größere Zahlen abgeschlossen zu werden. Es wird hier für veranschauliche Zwecke verwendet, um einen Vorgang anzuzeigen, der lange Verzögerungen in Ihrer Anwendung führen kann.

    // 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 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.
    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
    
  3. Fügen Sie im DoWork Ereignishandler einen Aufruf der ComputeFibonacci Methode hinzu. Nehmen Sie den ersten Parameter für ComputeFibonacci, aus der Argument-Eigenschaft der DoWorkEventArgs. Der BackgroundWorker- und DoWorkEventArgs-Parameter werden später zur Unterstützung von Fortschrittsberichten und zur Abbruchunterstützung verwendet. Weisen Sie den Rückgabewert von ComputeFibonacci der Eigenschaft Result des DoWorkEventArgs zu. Dieses Ergebnis steht dem RunWorkerCompleted Ereignishandler zur Verfügung.

    Hinweis

    Der DoWork Ereignishandler verweist nicht direkt auf die backgroundWorker1 Instanzvariable, da dies den Ereignishandler an eine bestimmte Instanz von BackgroundWorker koppeln würde. Stattdessen wird ein Verweis auf das BackgroundWorker, das dieses Ereignis ausgelöst hat, aus dem sender Parameter abgerufen. Dies ist wichtig, wenn das Formular mehr als ein BackgroundWorker beinhaltet. Es ist auch wichtig, keine Benutzeroberflächenobjekte in Ihrem DoWork Ereignishandler zu bearbeiten. Kommunizieren Sie stattdessen mit der Benutzeroberfläche über die BackgroundWorker-Ereignisse.

    // This event handler is where the actual,
    // potentially time-consuming work is done.
    void backgroundWorker1_DoWork( Object^ sender, DoWorkEventArgs^ e )
    {
       // Get the BackgroundWorker that raised this event.
       BackgroundWorker^ worker = dynamic_cast<BackgroundWorker^>(sender);
    
       // Assign the result of the computation
       // to the Result property of the DoWorkEventArgs
       // object. This is will be available to the 
       // RunWorkerCompleted eventhandler.
       e->Result = ComputeFibonacci( safe_cast<Int32>(e->Argument), worker, e );
    }
    
    // This event handler is where the actual,
    // potentially time-consuming work is done.
    private void backgroundWorker1_DoWork(object sender,
        DoWorkEventArgs e)
    {
        // Get the BackgroundWorker that raised this event.
        BackgroundWorker worker = sender as BackgroundWorker;
    
        // Assign the result of the computation
        // to the Result property of the DoWorkEventArgs
        // object. This is will be available to the
        // RunWorkerCompleted eventhandler.
        e.Result = ComputeFibonacci((int)e.Argument, worker, e);
    }
    
    ' This event handler is where the actual work is done.
    Private Sub backgroundWorker1_DoWork( _
    ByVal sender As Object, _
    ByVal e As DoWorkEventArgs) _
    Handles backgroundWorker1.DoWork
    
        ' Get the BackgroundWorker object that raised this event.
        Dim worker As BackgroundWorker = _
            CType(sender, BackgroundWorker)
    
        ' Assign the result of the computation
        ' to the Result property of the DoWorkEventArgs
        ' object. This is will be available to the 
        ' RunWorkerCompleted eventhandler.
        e.Result = ComputeFibonacci(e.Argument, worker, e)
    End Sub
    
  4. Fügen Sie im startAsyncButton Ereignishandler des Click Steuerelements den Code hinzu, der den asynchronen Vorgang startet.

    void startAsyncButton_Click( System::Object^ /*sender*/, System::EventArgs^ /*e*/ )
    {
       
       // Reset the text in the result label.
       resultLabel->Text = String::Empty;
    
       // Disable the UpDown control until 
       // the asynchronous operation is done.
       this->numericUpDown1->Enabled = false;
    
       // Disable the Start button until 
       // the asynchronous operation is done.
       this->startAsyncButton->Enabled = false;
    
       // Enable the Cancel button while 
       // the asynchronous operation runs.
       this->cancelAsyncButton->Enabled = true;
    
       // Get the value from the UpDown control.
       numberToCompute = (int)numericUpDown1->Value;
    
       // Reset the variable for percentage tracking.
       highestPercentageReached = 0;
    
       // Start the asynchronous operation.
       backgroundWorker1->RunWorkerAsync( numberToCompute );
    }
    
    private void startAsyncButton_Click(System.Object sender,
        System.EventArgs e)
    {
        // Reset the text in the result label.
        resultLabel.Text = String.Empty;
    
        // Disable the UpDown control until
        // the asynchronous operation is done.
        this.numericUpDown1.Enabled = false;
    
        // Disable the Start button until
        // the asynchronous operation is done.
        this.startAsyncButton.Enabled = false;
    
        // Enable the Cancel button while
        // the asynchronous operation runs.
        this.cancelAsyncButton.Enabled = true;
    
        // Get the value from the UpDown control.
        numberToCompute = (int)numericUpDown1.Value;
    
        // Reset the variable for percentage tracking.
        highestPercentageReached = 0;
    
        // Start the asynchronous operation.
        backgroundWorker1.RunWorkerAsync(numberToCompute);
    }
    
    Private Sub startAsyncButton_Click(ByVal sender As System.Object, _
    ByVal e As System.EventArgs) _
    Handles startAsyncButton.Click
    
        ' Reset the text in the result label.
        resultLabel.Text = [String].Empty
    
        ' Disable the UpDown control until 
        ' the asynchronous operation is done.
        Me.numericUpDown1.Enabled = False
    
        ' Disable the Start button until 
        ' the asynchronous operation is done.
        Me.startAsyncButton.Enabled = False
    
        ' Enable the Cancel button while 
        ' the asynchronous operation runs.
        Me.cancelAsyncButton.Enabled = True
    
        ' Get the value from the UpDown control.
        numberToCompute = CInt(numericUpDown1.Value)
    
        ' Reset the variable for percentage tracking.
        highestPercentageReached = 0
    
    
        ' Start the asynchronous operation.
        backgroundWorker1.RunWorkerAsync(numberToCompute)
    End Sub 
    
  5. Weisen Sie im RunWorkerCompleted-Ereignishandler das Ergebnis der Berechnung dem resultLabel-Steuerelement zu.

    // This event handler deals with the results of the
    // background operation.
    void backgroundWorker1_RunWorkerCompleted( Object^ /*sender*/, RunWorkerCompletedEventArgs^ e )
    {
       // First, handle the case where an exception was thrown.
       if ( e->Error != nullptr )
       {
          MessageBox::Show( e->Error->Message );
       }
       else
       if ( e->Cancelled )
       {
          // Next, handle the case where the user cancelled 
          // the operation.
          // Note that due to a race condition in 
          // the DoWork event handler, the Cancelled
          // flag may not have been set, even though
          // CancelAsync was called.
          resultLabel->Text = "Cancelled";
       }
       else
       {
          // Finally, handle the case where the operation 
          // succeeded.
          resultLabel->Text = e->Result->ToString();
       }
    
       // Enable the UpDown control.
       this->numericUpDown1->Enabled = true;
    
       // Enable the Start button.
       startAsyncButton->Enabled = true;
    
       // Disable the Cancel button.
       cancelAsyncButton->Enabled = false;
    }
    
    // This event handler deals with the results of the
    // background operation.
    private void backgroundWorker1_RunWorkerCompleted(
        object sender, RunWorkerCompletedEventArgs e)
    {
        // First, handle the case where an exception was thrown.
        if (e.Error != null)
        {
            MessageBox.Show(e.Error.Message);
        }
        else if (e.Cancelled)
        {
            // Next, handle the case where the user canceled
            // the operation.
            // Note that due to a race condition in
            // the DoWork event handler, the Cancelled
            // flag may not have been set, even though
            // CancelAsync was called.
            resultLabel.Text = "Canceled";
        }
        else
        {
            // Finally, handle the case where the operation
            // succeeded.
            resultLabel.Text = e.Result.ToString();
        }
    
        // Enable the UpDown control.
        this.numericUpDown1.Enabled = true;
    
        // Enable the Start button.
        startAsyncButton.Enabled = true;
    
        // Disable the Cancel button.
        cancelAsyncButton.Enabled = false;
    }
    
    ' This event handler deals with the results of the
    ' background operation.
    Private Sub backgroundWorker1_RunWorkerCompleted( _
    ByVal sender As Object, ByVal e As RunWorkerCompletedEventArgs) _
    Handles backgroundWorker1.RunWorkerCompleted
    
        ' First, handle the case where an exception was thrown.
        If (e.Error IsNot Nothing) Then
            MessageBox.Show(e.Error.Message)
        ElseIf e.Cancelled Then
            ' Next, handle the case where the user canceled the 
            ' operation.
            ' Note that due to a race condition in 
            ' the DoWork event handler, the Cancelled
            ' flag may not have been set, even though
            ' CancelAsync was called.
            resultLabel.Text = "Canceled"
        Else
            ' Finally, handle the case where the operation succeeded.
            resultLabel.Text = e.Result.ToString()
        End If
    
        ' Enable the UpDown control.
        Me.numericUpDown1.Enabled = True
    
        ' Enable the Start button.
        startAsyncButton.Enabled = True
    
        ' Disable the Cancel button.
        cancelAsyncButton.Enabled = False
    End Sub
    

Hinzufügen von Statusberichten und Support für Abbruch

Bei asynchronen Vorgängen, die eine lange Zeit dauern, ist es häufig wünschenswert, den Fortschritt an den Benutzer zu melden und dem Benutzer das Abbrechen des Vorgangs zu ermöglichen. Die BackgroundWorker Klasse stellt ein Ereignis bereit, mit dem Sie den Fortschritt nach dem Fortsetzen des Hintergrundvorgangs posten können. Außerdem wird ein Kennzeichen bereitgestellt, mit dem Ihr Arbeitscode einen Aufruf von CancelAsync erkennen und sich selbst unterbrechen kann.

Fortschrittsbericht implementieren

  1. Wählen Sie im Fenster "Eigenschaften" die Option backgroundWorker1aus. Legen Sie die Eigenschaften WorkerReportsProgress und WorkerSupportsCancellation auf true fest.

  2. Deklarieren Sie zwei Variablen im FibonacciCalculator Formular. Diese werden verwendet, um den Fortschritt nachzuverfolgen.

    int numberToCompute;
    int highestPercentageReached;
    
    private int numberToCompute = 0;
    private int highestPercentageReached = 0;
    
    Private numberToCompute As Integer = 0
    Private highestPercentageReached As Integer = 0
    
  3. Fügen Sie einen Ereignishandler für das ProgressChanged Ereignis hinzu. Aktualisieren Sie im ProgressChanged Ereignishandler ProgressBar mit der Eigenschaft ProgressPercentage des ProgressChangedEventArgs Parameters.

    // 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.Value = e.ProgressPercentage;
    }
    
    ' 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
    

Implementieren der Unterstützung für die Stornierung

  1. Fügen Sie im cancelAsyncButton Ereignishandler des Click Steuerelements den Code hinzu, der den asynchronen Vorgang abbricht.

    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(System.Object sender,
        System.EventArgs e)
    {
        // Cancel the asynchronous operation.
        this.backgroundWorker1.CancelAsync();
    
        // Disable the Cancel button.
        cancelAsyncButton.Enabled = false;
    }
    
    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
    
  2. Die folgenden Codefragmente in der ComputeFibonacci Methode melden Fortschritt und unterstützen die Abbruchfunktion.

    if ( worker->CancellationPending )
    {
       e->Cancel = true;
    }
    
    if (worker.CancellationPending)
    {
        e.Cancel = true;
    }
    
    If worker.CancellationPending Then
        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 );
    }
    
    // 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);
    }
    
    ' 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
    

Kontrollpunkt

An diesem Punkt können Sie die Fibonacci-Rechneranwendung kompilieren und ausführen.

Drücken Sie F5 , um die Anwendung zu kompilieren und auszuführen.

Während die Berechnung im Hintergrund ausgeführt wird, zeigt ProgressBar den Fortschritt der Berechnung bis zum Abschluss an. Sie können den ausstehenden Vorgang auch abbrechen.

Bei kleinen Zahlen sollte die Berechnung sehr schnell sein, aber für größere Zahlen sollte eine spürbare Verzögerung angezeigt werden. Wenn Sie einen Wert von 30 oder höher eingeben, sollte je nach Geschwindigkeit des Computers eine Verzögerung von mehreren Sekunden angezeigt werden. Bei Werten, die größer als 40 sind, kann es Minuten oder Stunden dauern, bis die Berechnung abgeschlossen ist. Während der Rechner damit beschäftigt ist, eine große Fibonacci-Zahl zu berechnen, stellen Sie fest, dass Sie das Formular frei bewegen, minimieren, maximieren und sogar schließen können. Dies liegt daran, dass der Haupt-UI-Thread nicht auf den Abschluss der Berechnung wartet.

Nächste Schritte

Nachdem Sie nun ein Formular implementiert haben, das eine BackgroundWorker Komponente zum Ausführen einer Berechnung im Hintergrund verwendet, können Sie weitere Möglichkeiten für asynchrone Vorgänge untersuchen:

Siehe auch