Delen via


Overzicht: een formulier implementeren dat gebruikmaakt van een achtergrondbewerking

Als het lang duurt voordat een bewerking is voltooid en u niet wilt dat uw gebruikersinterface (UI) niet meer reageert of blokkeert, kunt u de BackgroundWorker-klasse gebruiken om de bewerking op een andere thread uit te voeren.

In dit scenario ziet u hoe u de BackgroundWorker-klasse gebruikt om tijdrovende berekeningen op de achtergrond uit te voeren, terwijl de gebruikersinterface responsief blijft. Wanneer u klaar bent, hebt u een toepassing waarmee Fibonacci-getallen asynchroon worden berekend. Hoewel het berekenen van een groot Fibonacci-getal een merkbare hoeveelheid tijd kan duren, wordt de hoofd-UI-thread niet onderbroken door deze vertraging en reageert het formulier tijdens de berekening.

Taken die in deze walkthrough worden geïllustreerd, zijn:

  • Een Windows-toepassing maken

  • Een BackgroundWorker maken in je formulier

  • Asynchrone gebeurtenishandlers toevoegen

  • Voortgangsrapportage en ondersteuning toevoegen voor annulering

Zie How to: Implement a Form That Uses a Background Operationvoor een volledige lijst van de code die in dit voorbeeld wordt gebruikt.

Een formulier maken dat gebruikmaakt van een achtergrondbewerking

  1. Maak in Visual Studio een Windows-toepassingsproject met de naam BackgroundWorkerExample (File>New>Project>Visual C# of Visual Basic>Classic Desktop>Windows Forms Application).

  2. Klik in Solution Explorermet de rechtermuisknop op Form1 en selecteer Hernoemen in het snelmenu. Wijzig de bestandsnaam in FibonacciCalculator. Klik op de knop Ja wanneer u wordt gevraagd of u de naam van alle verwijzingen naar het code-elementForm1wilt wijzigen.

  3. Sleep een NumericUpDown besturingselement uit de Toolbox op het formulier. Stel de eigenschap Minimum in op 1 en de eigenschap Maximum op 91.

  4. Voeg twee Button besturingselementen toe aan het formulier.

  5. Wijzig de naam van het eerste Button besturingselement startAsyncButton en stel de eigenschap Text in op Start Async. Wijzig de naam van het tweede Button besturingselement cancelAsyncButtonen stel de eigenschap Text in op Cancel Async. Stel de eigenschap Enabled ervan in op false.

  6. Maak een event-handler voor zowel de Button-besturingselementen als de Click-gebeurtenissen. Zie Procedure: Gebeurtenis-handlers maken met de ontwerpfunctievoor meer informatie.

  7. Sleep een Label-besturingselement uit de gereedschapskist naar het formulier en hernoem het resultLabel.

  8. Sleep een ProgressBar besturingselement uit de Werkset naar het formulier.

Een BackgroundWorker maken met de Designer

U kunt de BackgroundWorker voor uw asynchrone bewerking maken met behulp van de WindowsForms Designer.

Sleep vanaf het tabblad Onderdelen van de Werkseteen BackgroundWorker naar het formulier.

Asynchrone gebeurtenis-handlers toevoegen

U bent nu klaar om eventhandlers toe te voegen voor de asynchrone gebeurtenissen van de BackgroundWorker component. De tijdrovende bewerking die op de achtergrond wordt uitgevoerd, waarmee Fibonacci-getallen worden berekend, wordt aangeroepen door een van deze gebeurtenis-handlers.

  1. Klik in het venster Eigenschappen, terwijl het BackgroundWorker onderdeel nog steeds is geselecteerd, op de knop Gebeurtenissen. Dubbelklik op de DoWork en RunWorkerCompleted gebeurtenissen om gebeurtenis-handlers te maken. Voor meer informatie over hoe gebeurtenishandlers te gebruiken, zie Gebeurtenishandlers maken met behulp van de ontwerper.

  2. Maak in uw formulier een nieuwe methode met de naam ComputeFibonacci. Met deze methode wordt het eigenlijke werk gedaan, en het zal op de achtergrond draaien. Deze code demonstreert de recursieve implementatie van het Fibonacci-algoritme, wat met name inefficiënt is, wat exponentieel langer duurt om grotere getallen te voltooien. Het wordt hier gebruikt voor illustratieve doeleinden om een bewerking weer te geven die lange vertragingen in uw toepassing kan veroorzaken.

    // 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. Voeg in de DoWork gebeurtenis-handler een aanroep toe aan de ComputeFibonacci methode. Neem de eerste parameter voor ComputeFibonacci uit de eigenschap Argument van het DoWorkEventArgs. De parameters BackgroundWorker en DoWorkEventArgs worden later gebruikt voor voortgangsrapportage en annuleringsondersteuning. Wijs de retourwaarde van ComputeFibonacci toe aan de eigenschap Result van de DoWorkEventArgs. Dit resultaat is beschikbaar voor de RunWorkerCompleted gebeurtenis-handler.

    Notitie

    De DoWork gebeurtenis-handler verwijst niet rechtstreeks naar de backgroundWorker1 exemplaarvariabele, omdat hiermee deze gebeurtenis-handler wordt gekoppeld aan een specifiek exemplaar van BackgroundWorker. In plaats daarvan wordt een verwijzing naar de BackgroundWorker die deze gebeurtenis heeft gegenereerd, afgeleid van de parameter sender. Dit is belangrijk wanneer een formulier meer dan één BackgroundWorker's bevat. Het is ook belangrijk dat u geen gebruikersinterfaceobjecten in uw DoWork gebeurtenis-handler bewerkt. In plaats daarvan communiceert u met de gebruikersinterface via de BackgroundWorker gebeurtenissen.

    // 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. Voeg in de event-handler voor de Click gebeurtenis van het startAsyncButton besturingselement de code toe waarmee de asynchrone bewerking wordt gestart.

    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. Wijs in de RunWorkerCompleted gebeurtenis-handler het resultaat van de berekening toe aan het besturingselement resultLabel.

    // 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
    

Voortgangsrapportage en ondersteuning toevoegen voor annulering

Voor asynchrone bewerkingen die lang duren, is het vaak wenselijk om de voortgang aan de gebruiker te rapporteren en de gebruiker de bewerking te laten annuleren. De BackgroundWorker-klasse biedt een gebeurtenis waarmee u de voortgang kunt posten naarmate de achtergrondbewerking wordt voortgezet. Het biedt ook een vlag waarmee uw werknemerscode een oproep naar CancelAsync kan detecteren en zichzelf kan stoppen.

Voortgangsrapportage implementeren

  1. In het venster Eigenschappen, selecteer backgroundWorker1. Stel de eigenschappen WorkerReportsProgress en WorkerSupportsCancellation in op true.

  2. Declareer twee variabelen in het formulier FibonacciCalculator. Deze worden gebruikt om de voortgang bij te houden.

    int numberToCompute;
    int highestPercentageReached;
    
    private int numberToCompute = 0;
    private int highestPercentageReached = 0;
    
    Private numberToCompute As Integer = 0
    Private highestPercentageReached As Integer = 0
    
  3. Voeg een gebeurtenis-handler toe voor de ProgressChanged gebeurtenis. Werk in de ProgressChanged gebeurtenis-handler de ProgressBar bij met de eigenschap ProgressPercentage van de parameter ProgressChangedEventArgs.

    // 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
    

Ondersteuning voor annulering implementeren

  1. Voeg in de event-handler van het cancelAsyncButton-besturingselement voor Click de code toe die de asynchrone bewerking annuleert.

    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. De volgende codefragmenten in de ComputeFibonacci methode rapporteren voortgang en annulering van ondersteuning.

    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
    

Doorlaatpost

Op dit moment kunt u de Fibonacci Calculator-toepassing compileren en uitvoeren.

Druk op F5- om de toepassing te compileren en uit te voeren.

Terwijl de berekening op de achtergrond wordt uitgevoerd, ziet u ProgressBar de voortgang van de berekening naar voltooiing toe tonen. U kunt de bewerking die in behandeling is ook annuleren.

Voor kleine getallen moet de berekening zeer snel zijn, maar voor grotere getallen ziet u een merkbare vertraging. Als u een waarde van 30 of hoger invoert, ziet u een vertraging van enkele seconden, afhankelijk van de snelheid van uw computer. Voor waarden die groter zijn dan 40, kan het enkele minuten of uren duren om de berekening te voltooien. Hoewel de rekenmachine bezig is met het berekenen van een groot Fibonacci-getal, ziet u dat u het formulier vrij kunt verplaatsen, minimaliseren, maximaliseren en zelfs negeren. Dit komt doordat de hoofd-UI-thread niet wacht tot de berekening is voltooid.

Volgende stappen

Nu u een formulier hebt geïmplementeerd dat gebruikmaakt van een BackgroundWorker-onderdeel om een berekening op de achtergrond uit te voeren, kunt u andere mogelijkheden voor asynchrone bewerkingen verkennen:

Zie ook