Exemplarische Vorgehensweise: Implementieren eines Formulars, das eine Hintergrundoperation verwendet
Wenn die Ausführung einer Operation sehr lange dauert und Sie verhindern möchten, dass die Benutzeroberfläche (UI) nicht mehr reagiert oder sich "aufhängt", können Sie mit der BackgroundWorker-Klasse die Operation mit einem anderen Thread ausführen.
Diese exemplarische Vorgehensweise veranschaulicht, wie die BackgroundWorker-Klasse zur Durchführung zeitintensiver Berechnungen "im Hintergrund" verwendet wird, während die Benutzeroberfläche weiterhin reagiert. Anschließend verfügen Sie über eine Anwendung, die Fibonacci-Zahlen asynchron berechnet. Auch wenn die Berechnung einer großen Fibonacci-Zahl recht lange dauern kann, wird der primäre UI-Thread von dieser Verzögerung nicht unterbrochen, sodass das Formular auch während der Berechnung auf Eingaben reagiert.
Zu den Aufgaben in dieser exemplarischen Vorgehensweise gehören:
Erstellen einer Windows-basierten Anwendung
Erstellen eines BackgroundWorker im Formular
Hinzufügen von asynchronen Ereignishandlern
Hinzufügen von Fortschrittsberichterstellung und Abbruchunterstützung
Eine vollständige Liste des in diesem Beispiel verwendeten Codes finden Sie unter Gewusst wie: Implementieren eines Formulars, das eine Hintergrundoperation verwendet.
Hinweis
Je nach den aktiven Einstellungen oder der verwendeten Version können die angezeigten Dialogfelder und Menübefehle von den in der Hilfe beschriebenen abweichen. Klicken Sie im Menü Extras auf Einstellungen importieren und exportieren, um die Einstellungen zu ändern. Weitere Informationen finden Sie unter Visual Studio-Einstellungen.
Erstellen des Projekts
Zunächst wird das Projekt erstellt und das Formular eingerichtet.
So erstellen Sie ein Formular, das eine Hintergrundoperation verwendet
Erstellen Sie ein Windows-basiertes Anwendungsprojekt mit dem Namen BackgroundWorkerExample. Ausführliche Informationen finden Sie unter Gewusst wie: Erstellen eines Windows-Anwendungsprojekts.
Klicken Sie im Projektmappen-Explorer mit der rechten Maustaste auf Form1 und dann im Kontextmenü auf Umbenennen. Ändern Sie den Dateinamen in FibonacciCalculator. Klicken Sie auf die Schaltfläche Ja, wenn Sie gefragt werden, ob Sie alle Verweise auf das Codeelement '
Form1
' umbenennen möchten.Ziehen Sie ein NumericUpDown-Steuerelement aus der Toolbox auf das Formular. Legen Sie die Minimum-Eigenschaft auf
1
und die Maximum-Eigenschaft auf91
fest.Fügen Sie dem Formular zwei Button-Steuerelemente hinzu.
Benennen Sie das erste Button-Steuerelement in
startAsyncButton
um, und legen Sie die Text-Eigenschaft aufStart Async
fest. Benennen Sie das zweite Button-Steuerelement incancelAsyncButton
um, und legen Sie die Text-Eigenschaft aufCancel Async
fest. Legen Sie die entsprechende Enabled-Eigenschaft auf false fest.Erstellen Sie einen Ereignishandler für beide Click-Ereignisse des Button-Steuerelements. Ausführliche Informationen finden Sie unter Gewusst wie: Erstellen von Ereignishandlern mithilfe des Designers.
Ziehen Sie ein Label-Steuerelement aus der Toolbox auf das Formular, und benennen Sie es in
resultLabel
um.Ziehen Sie ein ProgressBar-Steuerelement aus der Toolbox auf das Formular.
Erstellen eines BackgroundWorker im Formular
Sie können den BackgroundWorker für die asynchrone Operation mit dem Windows Forms-Designer erstellen.
So erstellen Sie einen BackgroundWorker mithilfe des Designers
- Ziehen Sie von der Registerkarte Komponenten der Toolbox einen BackgroundWorker auf das Formular.
Hinzufügen von asynchronen Ereignishandlern
Sie sind nun bereit, Ereignishandler für die asynchronen Ereignisse der BackgroundWorker-Komponente hinzuzufügen. Die zeitintensive Operation, die im Hintergrund ausgeführt wird und Fibonacci-Zahlen berechnet, wird von einem dieser Ereignishandler aufgerufen.
So implementieren Sie asynchrone Ereignishandler
Klicken Sie im Eigenschaftenfenster auf die Schaltfläche Ereignisse. Doppelklicken Sie auf das DoWork-Ereignis und das RunWorkerCompleted-Ereignis, um Ereignishandler zu erstellen. Weitere Informationen zur Verwendung von Ereignishandlern finden Sie unter Gewusst wie: Erstellen von Ereignishandlern mithilfe des Designers.
Erstellen Sie im Formular eine neue Methode mit dem Namen
ComputeFibonacci
. Diese Methode erledigt die eigentliche Arbeit und wird im Hintergrund ausgeführt. Dieser Code veranschaulicht die rekursive Umsetzung des Fibonacci-Algorithmus, der deutlich ineffizient und bei großen Zahlen exponentiell langsamer ist. Er wird hier verwendet, um eine Operation zu zeigen, die Ihre Anwendung deutlich verlangsamen 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. 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; }
Fügen Sie im DoWork-Ereignishandler der
ComputeFibonacci
-Methode einen Aufruf hinzu. Übernehmen Sie den ersten Parameter fürComputeFibonacci
von der Argument-Eigenschaft des DoWorkEventArgs. Der BackgroundWorker-Parameter und der DoWorkEventArgs-Parameter werden später zur Fortschrittsberichterstellung und Abbruchunterstützung verwendet.
Hinweis
Es ist wichtig, dass der DoWork-Ereignishandler nicht direkt auf die backgroundWorker1
-Instanz verweist, da dieser Ereignishandler ansonsten mit einer Instanz von BackgroundWorker gekoppelt wird. Stattdessen wird ein Verweis auf den BackgroundWorker, der dieses Ereignis ausgelöst hat, vom sender-Parameter wiederhergestellt. Dies ist wichtig, wenn das Formular mehr als einen BackgroundWorker hostet.
Weisen Sie der Result-Eigenschaft von DoWorkEventArgs den Rückgabewert von ComputeFibonacci
zu. Dieses Ergebnis steht dem RunWorkerCompleted-Ereignishandler zur Verfügung.
Hinzufügen von Fortschrittsberichterstellung und Abbruchunterstützung
Bei asynchronen Operationen, die lange dauern, ist es häufig wünschenswert, dem Benutzer den Fortschritt mitzuteilen und es ihm zu ermöglichen, die Operation abzubrechen. Die BackgroundWorker-Klasse stellt ein Ereignis bereit, mit dem Sie den Fortschritt aufzeichnen können, während die Operation im Hintergrund ausgeführt wird. Darüber hinaus verfügt sie über ein Flag, mit dem der Workercode einen Aufruf an CancelAsync erkennen und sich selbst unterbrechen kann.
So implementieren Sie die Fortschrittsberichterstellung
Wählen Sie im Eigenschaftenfenster
backgroundWorker1
aus. Legen Sie die WorkerReportsProgress-Eigenschaft und die WorkerSupportsCancellation-Eigenschaft auf true fest.Deklarieren Sie zwei Variablen im
FibonacciCalculator
-Formular. Diese werden verwendet, um den Fortschritt zu verfolgen.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;
Fügen Sie einen Ereignishandler für das ProgressChanged-Ereignis hinzu. Aktualisieren Sie im ProgressChanged-Ereignishandler die ProgressBar mit der ProgressPercentage-Eigenschaft des ProgressChangedEventArgs-Parameters.
' 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
So implementieren Sie die Abbruchunterstützung
Fügen Sie im Click-Ereignishandler des
cancelAsyncButton
-Steuerelements den Code hinzu, mit dem die asynchrone Operation abgebrochen wird.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); }
Die folgenden Codefragmente in der
ComputeFibonacci
-Methode dienen zur Fortschrittsberichterstellung und Abbruchunterstützung.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); }
Checkpoint
An diesem Punkt können Sie die Fibonacci-Berechnung kompilieren und ausführen.
So testen Sie das Projekt
Drücken Sie F5, um die Anwendung zu kompilieren und auszuführen.
Während die Berechnung ausgeführt wird, wird in der ProgressBar der Fortschritt der Berechnung angezeigt. Sie können die anstehende Operation auch abbrechen.
Bei kleinen Zahlen sollte die Berechnung sehr schnell gehen, während bei großen Zahlen eine deutliche Verzögerung erkennbar ist. Wenn Sie einen Wert von 30 oder höher eingeben, sollten Sie je nach Geschwindigkeit des Computers eine Verzögerung von mehreren Sekunden bemerken. Bei Werten über 40 dauert die Berechnung möglicherweise Minuten oder Stunden. Beachten Sie, dass Sie während der Berechnung einer großen Fibonacci-Zahl das Formular dennoch beliebig verschieben, minimieren, maximieren oder sogar schließen können. Der Grund dafür ist, dass der primäre UI-Thread nicht auf die Fertigstellung der Berechnung wartet.
Nächste Schritte
Nachdem Sie ein Formular implementiert haben, dass eine BackgroundWorker-Komponente für eine Berechnung im Hintergrund verwendet, können Sie weitere Möglichkeiten für asynchrone Operationen ausprobieren:
Verwenden Sie mehrere BackgroundWorker-Objekte für mehrere gleichzeitige Operationen.
Informationen zum Debuggen der Multithreadanwendung finden Sie unter Gewusst wie: Verwenden des Threadfensters.
Implementieren Sie Ihre eigene Komponente, die das asynchrone Programmiermodell unterstützt. Weitere Informationen finden Sie unter Übersicht über ereignisbasierte asynchrone Muster.
Warnung
Wenn Sie Multithreading verwenden, setzen Sie sich möglicherweise sehr ernsten und komplexen Problemen aus. Schlagen Sie unter Empfohlene Vorgehensweise für das verwaltete Threading nach, bevor Sie eine Projektmappe implementieren, die Multithreading verwendet.
Siehe auch
Aufgaben
Gewusst wie: Implementieren eines Formulars, das eine Hintergrundoperation verwendet
Exemplarische Vorgehensweise: Ausführen eines Vorgangs im Hintergrund
Referenz
Konzepte
Empfohlene Vorgehensweise für das verwaltete Threading
Weitere Ressourcen
Multithreading in Komponenten
Multithreading in Visual Basic
BackgroundWorker-Komponente