Hinweis
Für den Zugriff auf diese Seite ist eine Autorisierung erforderlich. Sie können versuchen, sich anzumelden oder das Verzeichnis zu wechseln.
Für den Zugriff auf diese Seite ist eine Autorisierung erforderlich. Sie können versuchen, das Verzeichnis zu wechseln.
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
Erstellen Sie in Visual Studio ein Windows-basiertes Anwendungsprojekt namens
BackgroundWorkerExample
"File>New>Project>Visual C# " oder "Visual Basic>Classic Desktop>Windows Forms Application".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.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
startAsyncButton
um, und legen Sie die Text Eigenschaft aufStart Async
. Benennen Sie das zweite Button SteuerelementcancelAsyncButton
um, und legen Sie die Text Eigenschaft aufCancel Async
. Legen Sie die zugehörige Enabled-Eigenschaft auffalse
fest.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.
Ziehen Sie ein Label Steuerelement aus der Toolbox auf das Formular, und benennen Sie es
resultLabel
um.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.
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.
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
Fügen Sie im DoWork Ereignishandler einen Aufruf der
ComputeFibonacci
Methode hinzu. Nehmen Sie den ersten Parameter fürComputeFibonacci
, 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 vonComputeFibonacci
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 demsender
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
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
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
Wählen Sie im Fenster "Eigenschaften" die Option
backgroundWorker1
aus. Legen Sie die Eigenschaften WorkerReportsProgress und WorkerSupportsCancellation auftrue
fest.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
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
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
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:
Verwenden Sie mehrere BackgroundWorker Objekte für mehrere gleichzeitige Vorgänge.
Informationen zum Debuggen Ihrer Multithreadanwendung finden Sie unter How to: Use the Threads Window.
Implementieren Sie Ihre eigene Komponente, die das asynchrone Programmiermodell unterstützt. Weitere Informationen finden Sie unter "Übersicht über ereignisbasiertes asynchrones Muster".
Vorsicht
Wenn Sie Multithreading jeglicher Art verwenden, machen Sie sich potenziell sehr schwerwiegenden und komplexen Fehlern aus. Lesen Sie die Best Practices für verwaltetes Threading, bevor Sie eine Lösung implementieren, die Multithreading verwendet.
Siehe auch
- System.ComponentModel.BackgroundWorker
- Verwaltetes Thread-Management
- Bewährte Methoden für verwaltetes Threading
- Übersicht über ereignisbasierte asynchrone Muster
- Vorgehensweise: Implementieren eines Formulars, das eine Hintergrundoperation verwendet
- Exemplarische Vorgehensweise: Ausführen eines Vorgangs im Hintergrund
- BackgroundWorker-Komponente
.NET Desktop feedback