Demonstra Passo a passo: Implementando um formulário que usa uma operação de plano de fundo
Se você tem uma operação que levará muito tempo para ser concluída e você não deseja que sua interface do usuário (UI) para parar de responder ou "travar", Você pode usar o BackgroundWorker classe para executar a operação em outro segmento.
Esta explicação passo a passo ilustra como usar o BackgroundWorker classe para executar as computações demoradas "em segundo plano," enquanto a interface do usuário continua a responder. Quando terminar, você terá um aplicativo que calcula números Fibonacci de forma assíncrona. Embora um grande número de Fibonacci pode levar uma quantidade considerável de tempo de computação, o principal segmento de interface do usuário não será interrompido por esse atraso e o formulário ser ágil durante o cálculo.
As tarefas ilustradas neste passo a passo incluem:
Criando um aplicativo baseado no Windows
Criando um BackgroundWorker no seu formulário.
Adicionando manipuladores de eventos assíncronos
Adicionando suporte a cancelamento e emissão de relatórios de andamento
Para obter uma listagem completa do código usada neste exemplo, consulte Como: Implementar um formulário que usa uma operação de plano de fundo.
Observação |
---|
As caixas de diálogo e comandos de menu demonstradas podem ser diferentes daqueles descritos na Ajuda, dependendo das configurações ativas ou configurações de edição. Para alterar as configurações, escolha Import and Export Settings sobre o Ferramentas menu. Para obter mais informações, consulte Trabalhando com configurações. |
Criando o projeto
A primeira etapa é criar o projeto e configurar o formulário.
Para criar um formulário que usa uma operação de plano de fundo
Crie um projeto de aplicativo do Windows chamado BackgroundWorkerExample. Para obter detalhes, consulte:Como: Crie um novo projeto de aplicativo do Windows Forms.
Em Solution Explorer, com o botão direito Form1 e selecione Renomear no menu de atalho. Altere o nome de arquivo para FibonacciCalculator. Clique o Sim botão quando for perguntado se deseja renomear todas as referências ao elemento de código 'Form1'.
Arrastar um NumericUpDown de controle da caixa de ferramentas para o formulário. Definir o Minimum propriedade para 1 e o Maximum propriedade para 91.
Adicione duas Button controles no formulário.
Renomeie o primeiro Button controle startAsyncButton e defina a Text propriedade para Start Async. Renomear a segunda Button controle cancelAsyncButtone defina a Text propriedade para Cancel Async. Defina seu Enabled propriedade para false.
Criar um manipulador de eventos para ambas as Button dos controles Clickeventos. Para obter detalhes, consulte:Como: Criar manipuladores de eventos usando o Designer.
Arrastar um Label de controle da caixa de ferramentas para o formulário e renomeá-lo resultLabel.
Arrastar um ProgressBar de controle da caixa de ferramentas para o formulário.
Criando um BackgroundWorker no seu formulário.
Você pode criar o BackgroundWorker para o uso de operação assíncrona de Windows Forms Designer.
Para criar o BackgroundWorker com o Designer
- Da componentes guia do caixa de ferramentas, arraste um BackgroundWorker até o formulário.
Adicionando manipuladores de eventos assíncronos
Agora você está pronto para adicionar manipuladores de eventos para o BackgroundWorker eventos assíncronos. do componente A operação demorada que serão executados em segundo plano, calcula os números de Fibonacci, é chamada por um desses manipuladores de eventos.
Implementar manipuladores de eventos assíncronos
No Propriedades janela, com o BackgroundWorker componente ainda selecionado, clique no eventos botão. Clique duas vezes o DoWork e RunWorkerCompleted eventos para criar manipuladores de eventos. Para obter mais informações sobre como usar os manipuladores de eventos, consulte Como: Criar manipuladores de eventos usando o Designer.
Criar um novo método, chamado ComputeFibonacci, no formulário. Esse método faz o trabalho real e ele será executado em segundo plano. Esse código demonstra a implementação de recursivo do algoritmo de Fibonacci, que é notavelmente ineficiente, demorando mais tempo para concluir para números maiores. Ela é usada aqui para fins ilustrativos, para mostrar uma operação que pode introduzir longos atrasos em seu aplicativo.
' 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; }
No DoWork o manipulador de eventos, adicione uma chamada para o ComputeFibonacci método. Levar o primeiro parâmetro para ComputeFibonacci da Argument propriedade da DoWorkEventArgs. O BackgroundWorker e DoWorkEventArgs parâmetros serão usados posteriormente para progresso relatórios e cancelamento de suporte. Atribuir o valor de retorno de ComputeFibonacci para o Result propriedade da DoWorkEventArgs. Esse resultado estarão disponível para o RunWorkerCompleted manipulador de eventos.
Observação O DoWork o manipulador de evento não faz referência a backgroundWorker1 diretamente, a variável de instância, pois isso seria associam esse manipulador de eventos para uma instância específica de BackgroundWorker. Em vez disso, uma referência para o BackgroundWorker que gerou esse evento é recuperado da sender parâmetro. Isso é importante quando o formulário hospeda mais de um BackgroundWorker. Também é importante não manipular quaisquer objetos de interface do usuário em seu DoWork manipulador de eventos. Em vez disso, se comunicar com a interface do usuário por meio de BackgroundWorker eventos.
' 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 'backgroundWorker1_DoWork
// 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, // 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 ); }
No startAsyncButton do controle Click o manipulador de eventos, adicione o código que inicia a operação assíncrona.
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
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); }
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 ); }
No RunWorkerCompleted o manipulador de eventos, atribui o resultado do cálculo para o resultLabel de controle.
' 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 'backgroundWorker1_RunWorkerCompleted
// 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. 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; }
Adicionando suporte a cancelamento e emissão de relatórios de andamento
Operações assíncronas que levarão muito tempo, muitas vezes é desejável para relatar o progresso ao usuário e permitir que o usuário cancelar a operação. O BackgroundWorker classe fornece um evento que permite que você envie o andamento como seu plano de fundo operação continua. Ele também fornece um sinalizador que permite que seu código de operador detectar uma chamada para CancelAsync e interromper a mesmo.
Para implementar o relatório de andamento
No Propriedades, janela, selecione backgroundWorker1. Definir as propriedades WorkerReportsProgress e WorkerSupportsCancellation para true.
Declare duas variáveis na FibonacciCalculator formulário. Essas serão usadas para controlar o progresso.
Private numberToCompute As Integer = 0 Private highestPercentageReached As Integer = 0
private int numberToCompute = 0; private int highestPercentageReached = 0;
int numberToCompute; int highestPercentageReached;
Adiciona um manipulador de eventos para o ProgressChanged de evento. No ProgressChanged manipulador de eventos, a atualização do ProgressBar com o ProgressPercentage propriedade do ProgressChangedEventArgs parâmetro.
' 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; }
Para implementar o suporte a cancelamento
No cancelAsyncButton do controle Click o manipulador de eventos, adicione o código que cancela a operação assíncrona.
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; }
Fragmentos de código a seguir no ComputeFibonacci método relatar o progresso e suporte ao cancelamento.
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 ); }
Ponto de Verificação
Neste ponto, você pode compilar e executar o aplicativo Calculadora de Fibonacci.
Para testar seu projeto.
Pressione F5 para compilar e executar o aplicativo.
Enquanto o cálculo é executado em segundo plano, você verá o ProgressBar Exibir o progresso do cálculo em direção a conclusão. Você também pode cancelar a operação pendente.
Para pequenas quantidades, o cálculo deve ser muito rápido, mas para números maiores, você deverá ver um atraso considerável. Se você inserir um valor de 30 ou maior, você deverá ver um atraso de alguns segundos, dependendo da velocidade do seu computador. Valores maiores do que 40, pode levar minutos ou horas para concluir o cálculo. Enquanto a calculadora está ocupada de um grande número de Fibonacci de computação, observe que você pode livremente mover o formulário, minimizar, maximizar e até mesmo descartá-lo. Isso ocorre porque o thread principal da interface do usuário não está aguardando o cálculo concluir.
Próximas etapas
Agora que você implementou um formulário que usa um BackgroundWorker o componente para executar um cálculo em segundo plano, você pode explorar outras possibilidades para operações assíncronas:
Usar várias BackgroundWorker objetos para várias operações simultâneas.
Para depurar seu aplicativo multithread, consulte Como: Use a janela Threads.
Implemente seu próprio componente que suporta o modelo de programação assíncrono. Para obter mais informações, consulte Event-based Asynchronous Pattern Overview.
Aviso
Ao usar multithreading de qualquer tipo, potencialmente expor você mesmo a bugs muito sérios e complexos. Consulte o Práticas recomendadas de threads gerenciadas antes de implementar qualquer solução que usa multithreading.
Consulte também
Tarefas
Como: Implementar um formulário que usa uma operação de plano de fundo
Demonstra Passo a passo: Executa uma operação em segundo plano
Referência
Conceitos
Práticas recomendadas de threads gerenciadas