Anteckning
Åtkomst till den här sidan kräver auktorisering. Du kan prova att logga in eller ändra kataloger.
Åtkomst till den här sidan kräver auktorisering. Du kan prova att ändra kataloger.
Om du har en åtgärd som tar lång tid att slutföra och du inte vill att användargränssnittet ska sluta svara eller blockera kan du använda BackgroundWorker klassen för att köra åtgärden på en annan tråd.
Den här genomgången BackgroundWorker visar hur du använder klassen för att utföra tidskrävande beräkningar "i bakgrunden", medan användargränssnittet förblir responsivt. När du är klar har du ett program som beräknar Fibonacci-tal asynkront. Även om det kan ta en märkbar tid att beräkna ett stort Fibonacci-nummer avbryts inte huvudtråden i användargränssnittet av den här fördröjningen, och formuläret svarar under beräkningen.
Uppgifter som illustreras i den här genomgången är:
Skapa ett Windows-baserat program
Skapa en BackgroundWorker i formuläret
Lägga till asynkrona händelsehanterare
Lägga till förloppsrapportering och support för annullering
En fullständig lista över koden som används i det här exemplet finns i Så här: Implementera ett formulär som använder en bakgrundsåtgärd.
Skapa ett formulär som använder en bakgrundsåtgärd
I Visual Studio skapar du ett Windows-baserat programprojekt med namnet
BackgroundWorkerExample(File>New>Project>Visual C# eller Visual Basic>Classic Desktop>Windows Forms Application).Högerklicka på Formulär1 i Solution Explorer och välj Byt namn på snabbmenyn. Ändra filnamnet till
FibonacciCalculator. Klicka på knappen Ja när du tillfrågas om du vill byta namn på alla referenser till kodelementetForm1.Dra en NumericUpDown kontroll från verktygslådan till formuläret. Ange egenskapen Minimum till
1och egenskapen Maximum till91.Lägg till två Button kontroller i formuläret.
Byt namn på den första Button kontrollen
startAsyncButtonoch ange egenskapen Text tillStart Async. Byt namn på den andra Button-kontrollen tillcancelAsyncButton, och ange Text-egenskapen tillCancel Async. Ange egenskapen Enabled tillfalse.Skapa en händelsehanterare för båda Button kontrollernas Click händelser. Mer information finns i Så här skapar du händelsehanterare med hjälp av designern.
Dra en Label kontroll från verktygslådan till formuläret och byt namn på den
resultLabel.Dra en ProgressBar kontroll från verktygslådan till formuläret.
Skapa en BackgroundWorker med designern
Du kan skapa BackgroundWorker för din asynkrona åtgärd med hjälp av WindowsForms Designer.
Dra en till formuläret på fliken Komponenter i BackgroundWorker.
Lägga till asynkrona händelsehanterare
Nu är du redo att lägga till händelsehanterare för komponentens BackgroundWorker asynkrona händelser. Den tidskrävande åtgärd som körs i bakgrunden, som beräknar Fibonacci-tal, anropas av en av dessa händelsehanterare.
I fönstret Egenskaper , med komponenten BackgroundWorker fortfarande markerad, klickar du på knappen Händelser . Dubbelklicka på DoWork händelserna och RunWorkerCompleted för att skapa händelsehanterare. Mer information om hur du använder händelsehanterare finns i Så här skapar du händelsehanterare med hjälp av designern.
Skapa en ny metod med namnet
ComputeFibonacci, i formuläret. Den här metoden utför det faktiska arbetet och körs i bakgrunden. Den här koden visar den rekursiva implementeringen av Fibonacci-algoritmen, som är särskilt ineffektiv, vilket tar exponentiellt längre tid att slutföra för större tal. Den används här i illustrativa syften för att visa en åtgärd som kan medföra långa fördröjningar i ditt program.// 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 FunctionDoWork Lägg till ett anrop till metoden i
ComputeFibonaccihändelsehanteraren. Ta den första parametern förComputeFibonaccifrån Argument egenskapen för DoWorkEventArgs. Parametrarna BackgroundWorker och DoWorkEventArgs används senare för förloppsrapportering och support för annullering. Tilldela returvärdet frånComputeFibonaccitill Result-egenskapen på DoWorkEventArgs. Det här resultatet blir tillgängligt för RunWorkerCompleted händelsehanteraren.Anmärkning
Händelsehanteraren DoWork refererar inte direkt till
backgroundWorker1instansvariabeln, eftersom detta skulle koppla den här händelsehanteraren till en specifik instans av BackgroundWorker. I stället återställs en referens till den BackgroundWorker som skapade den här händelsen från parameternsender. Detta är viktigt när formuläret innehåller mer än en BackgroundWorker. Det är också viktigt att inte ändra några användargränssnittsobjekt i händelsehanteraren DoWork . Kommunicera i stället med användargränssnittet via BackgroundWorker händelserna.// 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 SubI kontrollens
startAsyncButtonClick händelsehanterare lägger du till koden som startar den asynkrona åtgärden.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 SubRunWorkerCompleted I händelsehanteraren tilldelar du resultatet av beräkningen till
resultLabelkontrollen.// 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
Lägga till förloppsrapportering och support för annullering
För asynkrona åtgärder som tar lång tid är det ofta önskvärt att rapportera förloppet till användaren och låta användaren avbryta åtgärden. Klassen BackgroundWorker tillhandahåller en händelse som gör att du kan rapportera förloppet medan bakgrundsprocessen fortskrider. Den innehåller också en flagga som gör att din arbetskod kan identifiera ett anrop till CancelAsync och avbryta sig själv.
Implementera förloppsrapportering
I fönstret Egenskaper väljer du
backgroundWorker1. Ange egenskaperna WorkerReportsProgress och WorkerSupportsCancellation tilltrue.Deklarera två variabler i formuläret
FibonacciCalculator. Dessa används för att spåra förloppet.int numberToCompute; int highestPercentageReached;private int numberToCompute = 0; private int highestPercentageReached = 0;Private numberToCompute As Integer = 0 Private highestPercentageReached As Integer = 0Lägg till en händelsehanterare för ProgressChanged händelsen. I händelsehanteraren ProgressChanged, uppdaterar du ProgressBar med ProgressPercentage-egenskapen av ProgressChangedEventArgs-parametern.
// 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
Implementera stöd för annullering
I kontrollens
cancelAsyncButtonClick händelsehanterare lägger du till koden som avbryter den asynkrona åtgärden.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 SubFöljande kodfragment i
ComputeFibonaccimetoden rapporterar förlopp och stöd för annullering.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
Nu kan du kompilera och köra Fibonacci Calculator-programmet.
Tryck på F5 för att kompilera och köra programmet.
Medan beräkningen körs i bakgrunden, kommer du att se ProgressBar som visar beräkningens progression mot slutförande. Du kan också avbryta den pågående åtgärden.
För små tal bör beräkningen vara mycket snabb, men för större tal bör du se en märkbar fördröjning. Om du anger ett värde på 30 eller högre bör du se en fördröjning på flera sekunder, beroende på datorns hastighet. För värden som är större än 40 kan det ta minuter eller timmar att slutföra beräkningen. Medan kalkylatorn är upptagen med att beräkna ett stort Fibonacci-nummer, observera att du fritt kan flytta runt formuläret, minimera, maximera och till och med stänga det. Det beror på att huvudtråden för användargränssnittet inte väntar på att beräkningen ska slutföras.
Nästa steg
Nu när du har implementerat ett formulär som använder en BackgroundWorker komponent för att köra en beräkning i bakgrunden kan du utforska andra möjligheter för asynkrona åtgärder:
Använd flera BackgroundWorker objekt för flera samtidiga åtgärder.
Information om hur du felsöker ditt flertrådade program finns i Så här använder du fönstret Trådar.
Implementera din egen komponent som stöder den asynkrona programmeringsmodellen. Mer information finns i Händelsebaserad Asynkron mönsteröversikt.
Försiktighet
När du använder multitrådning av något slag kan du utsätta dig för mycket allvarliga och komplexa buggar. Läs bästa praxis för hanterad trådning innan du implementerar en lösning som använder multitrådning.
Se även
.NET Desktop feedback