Sdílet prostřednictvím


Návod: Implementace formuláře, který používá operaci na pozadí

Pokud máte operaci, která bude trvat dlouhou dobu a nechcete, aby uživatelské rozhraní přestalo reagovat nebo blokoval, můžete operaci spustit v jiném vlákně pomocí BackgroundWorker třídy.

Tento názorný postup ukazuje, jak pomocí BackgroundWorker třídy provádět časově náročné výpočty na pozadí, zatímco uživatelské rozhraní zůstává responzivní. Když procházíte, budete mít aplikaci, která vypočítá Fibonacci čísla asynchronně. I když výpočet velkého fibonacciho čísla může trvat značné množství času, hlavní vlákno uživatelského rozhraní nebude tímto zpožděním přerušeno a formulář bude reagovat během výpočtu.

Mezi úlohy znázorněné v tomto názorném postupu patří:

  • Vytvoření aplikace založené na Systému Windows

  • Vytvoření BackgroundWorker formuláře

  • Přidání asynchronních obslužných rutin událostí

  • Přidání vykazování průběhu a podpora zrušení

Úplný výpis kódu použitého v tomto příkladu naleznete v tématu Postupy: Implementace formuláře, který používá operaci pozadí.

Vytvoření formuláře, který používá operaci na pozadí

  1. V sadě Visual Studio vytvořte projekt aplikace založený na Systému Windows s názvem BackgroundWorkerExample (File>New>Project>Visual C# nebo Visual Basic>Classic Desktop> model Windows Forms Application).

  2. V Průzkumník řešení klikněte pravým tlačítkem na Formulář1 a v místní nabídce vyberte Přejmenovat. Změňte název souboru na FibonacciCalculator. Po zobrazení výzvy, zda chcete přejmenovat všechny odkazy na prvek kódu 'Form1' klikněte na tlačítko Ano.

  3. NumericUpDown Přetáhněte ovládací prvek ze sady nástrojů do formuláře. Minimum Nastavte vlastnost 1 na hodnotu a Maximum vlastnost na 91hodnotu .

  4. Přidejte do formuláře dva Button ovládací prvky.

  5. Přejmenujte první Button ovládací prvek startAsyncButton a nastavte Text vlastnost na Start Async. Přejmenujte druhý Button ovládací prvek cancelAsyncButtona nastavte Text vlastnost na Cancel Async. Nastavte jeho Enabled vlastnost na false.

  6. Vytvořte obslužnou rutinu události pro obě události ovládacích Button prvků Click . Podrobnosti najdete v tématu Postupy: Vytváření obslužných rutin událostí pomocí Návrháře.

  7. Label Přetáhněte ovládací prvek ze sady nástrojů do formuláře a přejmenujte ho resultLabel.

  8. ProgressBar Přetáhněte ovládací prvek ze sady nástrojů do formuláře.

Vytvoření práce na pozadí pomocí Návrháře

Asynchronní operaci můžete vytvořit BackgroundWorker pomocí WindowsForms Designeru.

Na kartě Součásti panelu nástrojů přetáhněte obrazce BackgroundWorker do formuláře.

Přidání asynchronních obslužných rutin událostí

Teď jste připraveni přidat obslužné rutiny událostí pro BackgroundWorker asynchronní události komponenty. Časově náročná operace, která se spustí na pozadí, která vypočítá fibonacciho čísla, je volána jedním z těchto obslužných rutin událostí.

  1. V okně Vlastnosti se stále vybranou komponentou BackgroundWorker klikněte na tlačítko Události . Poklikáním na DoWork události a RunWorkerCompleted vytvořte obslužné rutiny událostí. Další informace o tom, jak používat obslužné rutiny událostí, naleznete v tématu Postupy: Vytváření obslužných rutin událostí pomocí Návrháře.

  2. Ve formuláři vytvořte novou metodu s názvem ComputeFibonacci. Tato metoda provede skutečnou práci a spustí se na pozadí. Tento kód ukazuje rekurzivní implementaci Fibonacciho algoritmu, což je obzvláště neefektivní a exponenciálně delší dobu, než se dokončí pro větší čísla. Používá se zde pro ilustrativní účely k zobrazení operace, která může v aplikaci představovat dlouhá zpoždění.

    // 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
    
  3. V obslužné rutině DoWork události přidejte volání metody ComputeFibonacci . Vezměte první parametr z ComputeFibonacciArgument vlastnosti objektu DoWorkEventArgs. Parametry BackgroundWorker a DoWorkEventArgs parametry budou použity později pro podporu generování sestav průběhu a zrušení. Přiřaďte vrácenou hodnotu z ComputeFibonacciResult vlastnosti DoWorkEventArgs. Tento výsledek bude k dispozici pro obslužnou rutinu RunWorkerCompleted události.

    Poznámka:

    Obslužná rutina DoWork události neodkazuje přímo na backgroundWorker1 proměnnou instance, protože by tato obslužná rutina události s určitou instancí BackgroundWorker. Místo toho se z parametru obnoví odkaz na BackgroundWorker událost, která vyvolala sender tuto událost. To je důležité, když formulář hostuje více než jeden BackgroundWorker. Není také důležité manipulovat s žádnými objekty uživatelského rozhraní v DoWork obslužné rutině události. Místo toho komunikujte s uživatelským rozhraním prostřednictvím BackgroundWorker událostí.

    // 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
    
  4. startAsyncButton Do obslužné rutiny Click události ovládacího prvku přidejte kód, který spouští asynchronní operaci.

    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 
    
  5. V obslužné rutině RunWorkerCompleted události přiřaďte k ovládacímu resultLabel prvku výsledek výpočtu.

    // 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
    

Přidání vykazování průběhu a podpora zrušení

U asynchronních operací, které budou trvat dlouhou dobu, je často žádoucí hlásit průběh uživateli a umožnit uživateli zrušit operaci. Třída BackgroundWorker poskytuje událost, která umožňuje publikovat průběh operace na pozadí. Poskytuje také příznak, který umožňuje vašemu pracovnímu kódu rozpoznat volání CancelAsync a přerušit sám sebe.

Implementace generování sestav průběhu

  1. V okně Vlastnosti vyberte backgroundWorker1. Nastavte hodnotu WorkerReportsProgress a WorkerSupportsCancellation vlastnosti na truehodnotu .

  2. Deklarujte dvě proměnné ve formuláři FibonacciCalculator . Ty se použijí ke sledování průběhu.

    int numberToCompute;
    int highestPercentageReached;
    
    private int numberToCompute = 0;
    private int highestPercentageReached = 0;
    
    Private numberToCompute As Integer = 0
    Private highestPercentageReached As Integer = 0
    
  3. Přidejte obslužnou rutinu ProgressChanged události pro událost. V obslužné rutině ProgressChanged události aktualizujte ProgressBarProgressPercentage vlastnost parametru ProgressChangedEventArgs .

    // 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
    

Implementace podpory zrušení

  1. cancelAsyncButton Do obslužné rutiny události ovládacího prvku Click přidejte kód, který zruší asynchronní operaci.

    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
    
  2. Následující fragmenty kódu v ComputeFibonacci průběhu zprávy metody a podporují zrušení.

    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
    

CheckPoint

V tomto okamžiku můžete zkompilovat a spustit aplikaci Fibonacci Calculator.

Stisknutím klávesy F5 aplikaci zkompilujte a spusťte.

Zatímco výpočet běží na pozadí, zobrazí ProgressBar se průběh výpočtu směrem k dokončení. Můžete také zrušit čekající operaci.

U malých čísel by měl být výpočet velmi rychlý, ale u větších čísel byste měli vidět znatelné zpoždění. Pokud zadáte hodnotu 30 nebo vyšší, mělo by se v závislosti na rychlosti počítače zobrazit zpoždění několika sekund. U hodnot větších než 40 může dokončení výpočtu trvat několik minut nebo hodin. I když je kalkulačka zaneprázdněna výpočtem velkého Fibonacciho čísla, všimněte si, že můžete volně přesouvat formulář kolem, minimalizovat, maximalizovat a dokonce i zavřít. Důvodem je to, že hlavní vlákno uživatelského rozhraní nečeká na dokončení výpočtu.

Další kroky

Teď, když jste implementovali formulář, který používá komponentu BackgroundWorker ke spuštění výpočtu na pozadí, můžete prozkoumat další možnosti asynchronních operací:

Viz také