Share via


Procédure pas à pas : implémentation d'un formulaire qui utilise une opération d'arrière-plan

Mise à jour : novembre 2007

Si vous avez une opération dont l'exécution va prendre beaucoup de temps et que vous ne souhaitez pas que votre interface utilisateur cesse de répondre ou « se bloque », vous pouvez utiliser la classe BackgroundWorker pour exécuter l'opération sur un autre thread.

Cette procédure pas à pas illustre comment utiliser la classe BackgroundWorker pour exécuter « en arrière-plan » des calculs qui prennent du temps, pendant que l'interface utilisateur continue de répondre. Lorsque vous avez terminé, vous disposez alors d'une application qui calcule les nombres de Fibonacci de façon asynchrone. Si le calcul d'un nombre de Fibonacci élevé peut prendre un nombre important d'heures, le thread d'interface utilisateur principal n'est pas interrompu par ce délai, et le formulaire continu de répondre pendant le calcul.

Cette procédure pas à pas illustre les tâches suivantes :

  • Création d'une application Windows

  • Création d'un BackgroundWorker dans votre formulaire

  • Ajout de gestionnaires d'événements asynchrones

  • Ajout de rapport de progression et de prise en charge de l'annulation

Pour une liste complète du code utilisé dans cet exemple, consultez Comment : implémenter un formulaire qui utilise une opération d'arrière-plan.

Remarque :

Selon vos paramètres actifs ou votre édition, les boîtes de dialogue et les commandes de menu que vous voyez peuvent différer de celles qui sont décrites dans l'aide. Pour modifier vos paramètres, choisissez Importation et exportation de paramètres dans le menu Outils. Pour plus d'informations, consultez Paramètres Visual Studio.

Création du projet

La première étape consiste à créer le projet et à configurer le formulaire.

Pour créer un formulaire qui utilise une opération d'arrière-plan

  1. Créez un projet d'application Windows appelé BackgroundWorkerExample. Pour plus d'informations, consultez Comment : créer un projet d'application Windows.

  2. Dans l'Explorateur de solutions, cliquez avec le bouton droit sur Form1 et sélectionnez Renommer dans le menu contextuel. Remplacez le nom de fichier par FibonacciCalculator. Cliquez sur le bouton Oui à la question de savoir si vous souhaitez renommer toutes les références à l'élément de code "Form1".

  3. Faites glisser un contrôle NumericUpDown de la Boîte à outils vers le formulaire. Attribuez à la propriété Minimum la valeur 1 à et la propriété Maximum la valeur 91.

  4. Ajoutez deux contrôles Button au formulaire.

  5. Renommez le premier contrôle Button en startAsyncButton et attribuez à la propriété Text la valeur Start Async. Renommez le second contrôle Button en cancelAsyncButton et attribuez à la propriété Text la valeur Cancel Async. affectez à sa propriété Enabled la valeur false.

  6. Créez un gestionnaire d'événements pour les deux événements Click du contrôle Button. Pour plus d'informations, consultez Comment : créer des gestionnaires d'événements à l'aide du concepteur.

  7. Faites glisser un contrôle Label de la Boîte à outils sur le formulaire et renommez-le resultLabel.

  8. Faites glisser un contrôle ProgressBar de la Boîte à outils vers le formulaire.

Création d'un BackgroundWorker dans votre formulaire

Vous pouvez créer le BackgroundWorker pour votre opération asynchrone à l'aide du ConcepteurWindows Forms.

Pour créer un BackgroundWorker avec le concepteur

  • À partir de l'onglet Composants de la Boîte à outils, faites glisser un BackgroundWorker jusqu'au formulaire.

Ajout de gestionnaires d'événements asynchrones

Vous êtes maintenant prêt à ajouter des gestionnaires d'événements pour les événements asynchrones du composant BackgroundWorker. Cette longue opération qui s'exécute en arrière-plan, pour calculer les nombres de Fibonacci, est appelée par l'un de ces gestionnaires d'événements.

Pour implémenter des gestionnaires d'événements asynchrones

  1. Dans la fenêtre Propriétés, cliquez sur le bouton Événements. Double-cliquez sur les événements DoWork et RunWorkerCompleted pour créer des gestionnaires d'événements. Pour plus d'informations sur l'utilisation de gestionnaires d'événements, consultez Comment : créer des gestionnaires d'événements à l'aide du concepteur.

  2. Créez une nouvelle méthode, appelée ComputeFibonacci, dans votre formulaire. Cette méthode effectue le travail réel et s'exécute en arrière-plan. Ce code montre que l'implémentation récursive de l'algorithme de Fibonacci, qui est notamment inefficace, prend un temps exponentiellement plus long pour les grands nombres. Il est utilisé ici à des fins d'illustration pour montrer une opération qui peut introduire de longs délais dans votre application.

    ' 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. Dans le gestionnaire d'événements DoWork, ajoutez un appel à la méthode ComputeFibonacci. Prenez le premier paramètre pour ComputeFibonacci à partir de la propriété Argument du DoWorkEventArgs. Les paramètres BackgroundWorker et DoWorkEventArgs seront utilisés ultérieurement pour le rapport de progression et la prise en charge de l'annulation.

Remarque :

Il est important que le gestionnaire d'événements DoWork ne fasse pas référence directement à la variable d'instance de backgroundWorker1, car ceci associerait ce gestionnaire d'événements à une instance spécifique de BackgroundWorker. À la place, une référence au BackgroundWorker qui a déclenché cet événement est récupérée à partir du paramètre sender. Ceci est important lorsque le formulaire héberge plusieurs BackgroundWorker.

Assignez la valeur de retour de ComputeFibonacci à la propriété Result du DoWorkEventArgs. Ce résultat sera disponible au gestionnaire d'événements RunWorkerCompleted.

Ajout de rapport de progression et de prise en charge de l'annulation

Pour les opérations asynchrones qui prennent un certain temps, il est souvent souhaitable de signaler la progression à l'utilisateur et de permettre à celui-ci d'annuler l'opération. La classe BackgroundWorker fournit un événement qui vous permet de publier la progression comme résultat de l'opération d'arrière-plan. Il fournit également un indicateur qui permet à votre code de travail de détecter un appel à CancelAsync et l'interrompre.

Pour implémenter un rapport de progression

  1. Dans la fenêtre Propriétés, sélectionnez la propriété backgroundWorker1. Attribuez aux propriétés WorkerReportsProgress et WorkerSupportsCancellation la valeur true.

  2. Déclarez deux variables dans le formulaire FibonacciCalculator. Celles-ci seront utilisées pour suivre la progression.

    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. Ajoute un gestionnaire d'événements pour l'événement ProgressChanged. Dans le gestionnaire d'événements ProgressChanged, mettez à jour le ProgressBar avec la propriété ProgressPercentage du paramètre 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
    

Pour implémenter la prise en charge de l'annulation

  1. Dans le gestionnaire d'événements du contrôle cancelAsyncButtonClick, ajoutez le code qui annule l'opération asynchrone.

    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. Les fragments de code suivants dans le rapport de méthode ComputeFibonacci signalent la progression et prennent en charge l'annulation.

    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);
    }
    

Point de contrôle

À ce stade, vous pouvez compiler et exécuter l'application Fibonacci Calculator.

Pour tester le projet

  • Appuyez sur F5 pour compiler et exécuter l'application.

    Pendant que le calcul s'exécute en arrière-plan, vous pouvez consulter ProgressBar qui affiche la progression du calcul en voie d'achèvement. Vous pouvez également annuler l'opération en suspens.

    Pour les petits nombres, le calcul doit être très rapide, mais pour les grands nombres, vous devez constater un délai notable. Si vous entrez une valeur égale ou supérieure à 30, vous devez constater un délai de plusieurs secondes, selon la vitesse de votre ordinateur. Pour les valeurs supérieures à 40, le calcul peut durer plusieurs minutes, voire des heures. Pendant que la calculatrice est occupée à calculer un grand nombre de Fibonacci, notez que vous pouvez déplacer librement le formulaire, le réduire, l'agrandir et même le faire disparaître. L'explication en est simple : le thread d'interface utilisateur principal n'attend pas que le calcul se termine.

Étapes suivantes

Maintenant que vous avez implémenté un formulaire qui utilise un composant BackgroundWorker pour exécuter un calcul en arrière-plan, vous pouvez explorer d'autres possibilités pour les opérations asynchrones :

Voir aussi

Tâches

Comment : implémenter un formulaire qui utilise une opération d'arrière-plan

Procédure pas à pas : exécution d'une opération en arrière-plan

Concepts

Méthodes conseillées pour le threading managé

Référence

BackgroundWorker

Autres ressources

Multithreading dans les composants

Multithreading dans Visual Basic

BackgroundWorker, composant