Remarque
L’accès à cette page nécessite une autorisation. Vous pouvez essayer de vous connecter ou de modifier des répertoires.
L’accès à cette page nécessite une autorisation. Vous pouvez essayer de modifier des répertoires.
Si vous avez une opération qui prendra beaucoup de temps et que vous ne souhaitez pas que votre interface utilisateur cesse de répondre ou de bloquer, vous pouvez utiliser la BackgroundWorker classe pour exécuter l’opération sur un autre thread.
Cette procédure pas à pas montre comment utiliser la BackgroundWorker classe pour effectuer des calculs chronophages « en arrière-plan », tandis que l’interface utilisateur reste réactive. Lorsque vous aurez terminé, vous aurez une application qui calcule les nombres de Fibonacci de manière asynchrone. Même si le calcul d’un grand nombre Fibonacci peut prendre un certain temps, le thread d’interface utilisateur principal ne sera pas interrompu par ce délai et le formulaire sera réactif pendant le calcul.
Les tâches illustrées dans cette procédure pas à pas sont les suivantes :
Création d’une application Windows
Création d’un BackgroundWorker dans votre formulaire
Ajout de gestionnaires d’événements asynchrones
Ajout de rapports de progression et prise en charge de l’annulation
Pour obtenir une liste complète du code utilisé dans cet exemple, consultez Guide pratique pour implémenter un formulaire qui utilise une opération en arrière-plan.
Créer un formulaire qui utilise une opération d’arrière-plan
Dans Visual Studio, créez un projet d’application Windows appelé
BackgroundWorkerExample(File>New>Project>Visual C# ou Visual Basic>Classic Desktop>Application Windows Forms).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 lorsque vous êtes invité à renommer toutes les références à l’élément de code «Form1».Faites glisser un NumericUpDown contrôle de la boîte à outils sur le formulaire. Définissez la Minimum propriété sur
1et la Maximum propriété sur91.Ajoutez deux contrôles Button au formulaire.
Renommez le premier Button contrôle
startAsyncButtonet définissez la Text propriété surStart Async. Renommez le deuxième Button contrôlecancelAsyncButtonet définissez la Text propriété surCancel Async. Définissez sa propriété Enabled surfalse.Créez un gestionnaire d'événements pour les événements des deux contrôles ButtonClick. Pour plus d’informations, consultez Guide pratique pour créer des gestionnaires d’événements à l’aide du concepteur.
Faites glisser un Label contrôle de la boîte à outils sur le formulaire et renommez-le
resultLabel.Faites glisser un ProgressBar contrôle de la boîte à outils sur le formulaire.
Créer un BackgroundWorker avec le Concepteur
Vous pouvez créer le BackgroundWorker pour votre opération asynchrone à l’aide du Concepteur WindowsForms.
Sous l’onglet Composants de la boîte à outils, faites glisser un BackgroundWorker vers le formulaire.
Ajouter des gestionnaires d’événements asynchrones
Vous êtes maintenant prêt à ajouter des gestionnaires d’événements pour les BackgroundWorker événements asynchrones du composant. L’opération fastidieuse qui s’exécutera en arrière-plan, qui calcule les nombres Fibonacci, est appelée par l’un de ces gestionnaires d’événements.
Dans la fenêtre Propriétés , avec le BackgroundWorker composant toujours sélectionné, 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 des gestionnaires d’événements, consultez Guide pratique pour créer des gestionnaires d’événements à l’aide du concepteur.
Créez une 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 illustre l’implémentation récursive de l’algorithme Fibonacci, qui est particulièrement inefficace, prenant un temps exponentiel plus long pour un nombre plus grand. Il est utilisé ici à des fins d’illustration pour montrer une opération qui peut introduire de longs retards 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. 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 FunctionDans le gestionnaire d’événements DoWork , ajoutez un appel à la
ComputeFibonacciméthode. Prenez le premier paramètre pourComputeFibonaccila Argument propriété du DoWorkEventArgs. Les paramètres BackgroundWorker et DoWorkEventArgs seront utilisés ultérieurement pour le compte rendu de l'avancement et la gestion de l'annulation. Affectez la valeur de retour deComputeFibonaccià la propriété Result du DoWorkEventArgs. Ce résultat sera disponible pour le gestionnaire d’événements RunWorkerCompleted .Remarque
Le DoWork gestionnaire d’événements ne référence pas directement la
backgroundWorker1variable d’instance, car cela couplerait ce gestionnaire d’événements à une instance spécifique de BackgroundWorker. Au lieu de cela, une référence à l’événement BackgroundWorker déclenché est récupérée à partir dusenderparamètre. Cela est important lorsque le formulaire héberge plusieurs BackgroundWorker. Il est également important de ne pas manipuler d’objets d’interface utilisateur dans votre DoWork gestionnaire d’événements. Au lieu de cela, communiquez avec l’interface utilisateur via les BackgroundWorker événements.// 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 SubDans le
startAsyncButtongestionnaire d’événements du Click contrôle, ajoutez le code qui démarre l’opération asynchrone.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 SubDans le RunWorkerCompleted gestionnaire d’événements, affectez le résultat du calcul au
resultLabelcontrôle.// 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
Ajout de rapports de progression et prise en charge de l’annulation
Pour les opérations asynchrones qui prendront beaucoup de temps, il est souvent souhaitable de signaler la progression à l’utilisateur et de permettre à l’utilisateur d’annuler l’opération. La BackgroundWorker classe fournit un événement qui vous permet de publier la progression à mesure que votre opération en arrière-plan se poursuit. Il fournit également un indicateur qui permet à votre code de travail de détecter un appel à CancelAsync et de s'interrompre.
Implémenter des rapports de progression
Dans la fenêtre Propriétés, sélectionnez
backgroundWorker1. Définissez les propriétés WorkerReportsProgress et WorkerSupportsCancellation surtrue.Déclarez deux variables dans le
FibonacciCalculatorformulaire. Celles-ci seront utilisées pour suivre la progression.int numberToCompute; int highestPercentageReached;private int numberToCompute = 0; private int highestPercentageReached = 0;Private numberToCompute As Integer = 0 Private highestPercentageReached As Integer = 0Ajoutez un gestionnaire d’événements pour l’événement ProgressChanged . Dans le gestionnaire d’événements ProgressChanged, mettez à jour la propriété ProgressBar avec le paramètre 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.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
Implémenter la prise en charge de l’annulation
Dans le
cancelAsyncButtongestionnaire d’événements du Click contrôle, ajoutez le code qui annule l’opération asynchrone.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 SubLes fragments de code suivants dans la méthode
ComputeFibonaccirapportent la progression et prennent en charge l'annulation.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
Point de contrôle
À ce stade, vous pouvez compiler et exécuter l’application Fibonacci Calculator.
Appuyez sur F5 pour compiler et exécuter l’application.
Pendant que le calcul est en cours d’exécution en arrière-plan, vous verrez l’affichage ProgressBar de la progression du calcul vers la fin. Vous pouvez également annuler l’opération en attente.
Pour les petits nombres, le calcul doit être très rapide, mais pour les plus grands nombres, vous devriez voir un délai notable. Si vous entrez une valeur de 30 ou supérieure, vous devriez voir un délai de plusieurs secondes, en fonction de la vitesse de votre ordinateur. Pour les valeurs supérieures à 40, la fin du calcul peut prendre des minutes ou des heures. Alors que la calculatrice est occupée à calculer un grand nombre Fibonacci, notez que vous pouvez déplacer librement la forme autour, réduire, agrandir et même ignorer. Cela est dû au fait que le thread d’interface utilisateur principal n’attend pas la fin du calcul.
Étapes suivantes
Maintenant que vous avez implémenté un formulaire qui utilise un BackgroundWorker composant pour exécuter un calcul en arrière-plan, vous pouvez explorer d’autres possibilités pour les opérations asynchrones :
Utilisez plusieurs BackgroundWorker objets pour plusieurs opérations simultanées.
Pour déboguer votre application multithreadée, consultez Comment : utiliser la fenêtre des threads.
Implémentez votre propre composant qui prend en charge le modèle de programmation asynchrone. Pour plus d’informations, consultez Vue d’ensemble du modèle asynchrone basé sur les événements.
Avertissement
Lorsque vous utilisez le multithreading de n’importe quelle sorte, vous vous exposez potentiellement à des bogues très graves et complexes. Consultez les meilleures pratiques Managed Threading avant d’implémenter une solution qui utilise la multithreading.
Voir aussi
- System.ComponentModel.BackgroundWorker
- Threading managé
- Bonnes pratiques de threading managé
- Vue d’ensemble du modèle asynchrone basé sur les événements
- Comment : implémenter un formulaire qui utilise une opération en arrière-plan
- Procédure pas à pas : exécution d’une opération en arrière-plan
- Composant BackgroundWorker
.NET Desktop feedback