Instruções passo a passo: multithreading com o componente BackgroundWorker (C# e Visual Basic)
Esse passo a passo demonstra como criar uma aplicação com multithreading que procura ocorrências de uma palavra num arquivo de texto. Demonstra:
Como definir uma classe com um método que pode ser chamado pelo componente BackgroundWorker.
Como manipular eventos levantados pelo componente BackgroundWorker.
Como iniciar um componente BackgroundWorker para rodar um método.
Como implementar um botão Cancel que para o componente BackgroundWorker.
Para criar a interface do usuário
Abrir uma nova Visual Basic ou C# Windows Application project e crie um formulário denominado Form1.
Adicione dois botões e quatro caixas de texto a Form1.
Nomeie os objetos como mostrado na tabela a seguir.
Object
Propriedade
Configuração
Primeiro botão
Name, Text
Iniciar, Iniciar
Segundo Botão
Name, Text
Cancelar, Cancelar
Primeira caixa de texto
Name, Text
Arquivo Fonte, ""
Segunda caixa de texto
Name, Text
ComparaString, ""
Terceira caixa de texto
Name, Text
PalavrasContadas, "0"
Quarta caixa de texto
Name, Text
LinhasContadas, "0"
Adicione um rótulo para cada caixa de texto. Configure a propriedade Text para cada rótulo como mostrado na seguinte tabela.
Object
Propriedade
Configuração
Primeiro rótulo
Text
Source File
Segundo rótulo
Text
Comparar String
Terceiro rótulo
Text
Palavras correspondentes
Quarto rótulo
Text
Linhas Contadas
Para criar um componente BackgroundWorker e assinar seus eventos.
Adicionar um BackgroundWorker componente no componentes seção a caixa de ferramentas ao formulário. Isso vai aparecer na bandeja de componentes do formulário.
Defina as seguintes propriedades para o objeto BackgroundWorker1 na Visual Basic ou o objeto backgroundWorker1 em C#.
Propriedade
Configuração
WorkerReportsProgress
True
WorkerSupportsCancellation
True
No C# apenas, assine os eventos do objeto backgroundWorker1. Na parte superior a Propriedades janela, clique no eventos ícone. Clique duas vezes o RunWorkerCompleted evento para criar um método de manipulador de eventos. Faça o mesmo para o ProgressChanged e DoWork eventos.
Para definir o método que irá rodar num encadeamento diferente
No menu Project, escolha Add Class para adicionar uma classe ao projeto. A caixa de diálogo Add New Item é exibida.
Selecione classe da janela de modelos de e digite Words.vb ou Words.cs no campo nome.
Clique em Adicionar. A classe Words é mostrada.
Adicione o seguinte código à classe Words:
Public Class Words ' Object to store the current state, for passing to the caller. Public Class CurrentState Public LinesCounted As Integer Public WordsMatched As Integer End Class Public SourceFile As String Public CompareString As String Private WordCount As Integer = 0 Private LinesCounted As Integer = 0 Public Sub CountWords( ByVal worker As System.ComponentModel.BackgroundWorker, ByVal e As System.ComponentModel.DoWorkEventArgs ) ' Initialize the variables. Dim state As New CurrentState Dim line = "" Dim elapsedTime = 20 Dim lastReportDateTime = Now If CompareString Is Nothing OrElse CompareString = System.String.Empty Then Throw New Exception("CompareString not specified.") End If Using myStream As New System.IO.StreamReader(SourceFile) ' Process lines while there are lines remaining in the file. Do While Not myStream.EndOfStream If worker.CancellationPending Then e.Cancel = True Exit Do Else line = myStream.ReadLine WordCount += CountInString(line, CompareString) LinesCounted += 1 ' Raise an event so the form can monitor progress. If Now > lastReportDateTime.AddMilliseconds(elapsedTime) Then state.LinesCounted = LinesCounted state.WordsMatched = WordCount worker.ReportProgress(0, state) lastReportDateTime = Now End If ' Uncomment for testing. 'System.Threading.Thread.Sleep(5) End If Loop ' Report the final count values. state.LinesCounted = LinesCounted state.WordsMatched = WordCount worker.ReportProgress(0, state) End Using End Sub Private Function CountInString( ByVal SourceString As String, ByVal CompareString As String ) As Integer ' This function counts the number of times ' a word is found in a line. If SourceString Is Nothing Then Return 0 End If Dim EscapedCompareString = System.Text.RegularExpressions.Regex.Escape(CompareString) ' To count all occurrences of the string, even within words, remove ' both instances of "\b". Dim regex As New System.Text.RegularExpressions.Regex( "\b" + EscapedCompareString + "\b", System.Text.RegularExpressions.RegexOptions.IgnoreCase) Dim matches As System.Text.RegularExpressions.MatchCollection matches = regex.Matches(SourceString) Return matches.Count End Function End Class
public class Words { // Object to store the current state, for passing to the caller. public class CurrentState { public int LinesCounted; public int WordsMatched; } public string SourceFile; public string CompareString; private int WordCount; private int LinesCounted; public void CountWords( System.ComponentModel.BackgroundWorker worker, System.ComponentModel.DoWorkEventArgs e) { // Initialize the variables. CurrentState state = new CurrentState(); string line = ""; int elapsedTime = 20; DateTime lastReportDateTime = DateTime.Now; if (CompareString == null || CompareString == System.String.Empty) { throw new Exception("CompareString not specified."); } // Open a new stream. using (System.IO.StreamReader myStream = new System.IO.StreamReader(SourceFile)) { // Process lines while there are lines remaining in the file. while (!myStream.EndOfStream) { if (worker.CancellationPending) { e.Cancel = true; break; } else { line = myStream.ReadLine(); WordCount += CountInString(line, CompareString); LinesCounted += 1; // Raise an event so the form can monitor progress. int compare = DateTime.Compare( DateTime.Now, lastReportDateTime.AddMilliseconds(elapsedTime)); if (compare > 0) { state.LinesCounted = LinesCounted; state.WordsMatched = WordCount; worker.ReportProgress(0, state); lastReportDateTime = DateTime.Now; } } // Uncomment for testing. //System.Threading.Thread.Sleep(5); } // Report the final count values. state.LinesCounted = LinesCounted; state.WordsMatched = WordCount; worker.ReportProgress(0, state); } } private int CountInString( string SourceString, string CompareString) { // This function counts the number of times // a word is found in a line. if (SourceString == null) { return 0; } string EscapedCompareString = System.Text.RegularExpressions.Regex.Escape(CompareString); System.Text.RegularExpressions.Regex regex; regex = new System.Text.RegularExpressions.Regex( // To count all occurrences of the string, even within words, remove // both instances of @"\b" from the following line. @"\b" + EscapedCompareString + @"\b", System.Text.RegularExpressions.RegexOptions.IgnoreCase); System.Text.RegularExpressions.MatchCollection matches; matches = regex.Matches(SourceString); return matches.Count; } }
Para manipular eventos do encadeamento
Adicione os seguintes manipuladores de eventos ao seu formulário principal:
Private Sub BackgroundWorker1_RunWorkerCompleted( ByVal sender As Object, ByVal e As System.ComponentModel.RunWorkerCompletedEventArgs ) Handles BackgroundWorker1.RunWorkerCompleted ' This event handler is called when the background thread finishes. ' This method runs on the main thread. If e.Error IsNot Nothing Then MessageBox.Show("Error: " & e.Error.Message) ElseIf e.Cancelled Then MessageBox.Show("Word counting canceled.") Else MessageBox.Show("Finished counting words.") End If End Sub Private Sub BackgroundWorker1_ProgressChanged( ByVal sender As Object, ByVal e As System.ComponentModel.ProgressChangedEventArgs ) Handles BackgroundWorker1.ProgressChanged ' This method runs on the main thread. Dim state As Words.CurrentState = CType(e.UserState, Words.CurrentState) Me.LinesCounted.Text = state.LinesCounted.ToString Me.WordsCounted.Text = state.WordsMatched.ToString End Sub
private void backgroundWorker1_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e) { // This event handler is called when the background thread finishes. // This method runs on the main thread. if (e.Error != null) MessageBox.Show("Error: " + e.Error.Message); else if (e.Cancelled) MessageBox.Show("Word counting canceled."); else MessageBox.Show("Finished counting words."); } private void backgroundWorker1_ProgressChanged(object sender, ProgressChangedEventArgs e) { // This method runs on the main thread. Words.CurrentState state = (Words.CurrentState)e.UserState; this.LinesCounted.Text = state.LinesCounted.ToString(); this.WordsCounted.Text = state.WordsMatched.ToString(); }
Para iniciar e chamar um novo encadeamento que executa o método WordCount
Adicione o seguinte código ao seu programa:
Private Sub BackgroundWorker1_DoWork( ByVal sender As Object, ByVal e As System.ComponentModel.DoWorkEventArgs ) Handles BackgroundWorker1.DoWork ' This event handler is where the actual work is done. ' This method runs on the background thread. ' Get the BackgroundWorker object that raised this event. Dim worker As System.ComponentModel.BackgroundWorker worker = CType(sender, System.ComponentModel.BackgroundWorker) ' Get the Words object and call the main method. Dim WC As Words = CType(e.Argument, Words) WC.CountWords(worker, e) End Sub Sub StartThread() ' This method runs on the main thread. Me.WordsCounted.Text = "0" ' Initialize the object that the background worker calls. Dim WC As New Words WC.CompareString = Me.CompareString.Text WC.SourceFile = Me.SourceFile.Text ' Start the asynchronous operation. BackgroundWorker1.RunWorkerAsync(WC) End Sub
private void backgroundWorker1_DoWork(object sender, DoWorkEventArgs e) { // This event handler is where the actual work is done. // This method runs on the background thread. // Get the BackgroundWorker object that raised this event. System.ComponentModel.BackgroundWorker worker; worker = (System.ComponentModel.BackgroundWorker)sender; // Get the Words object and call the main method. Words WC = (Words)e.Argument; WC.CountWords(worker, e); } private void StartThread() { // This method runs on the main thread. this.WordsCounted.Text = "0"; // Initialize the object that the background worker calls. Words WC = new Words(); WC.CompareString = this.CompareString.Text; WC.SourceFile = this.SourceFile.Text; // Start the asynchronous operation. backgroundWorker1.RunWorkerAsync(WC); }
Chame o método StartThread do botão Start em seu formulário:
Private Sub Start_Click() Handles Start.Click StartThread() End Sub
private void Start_Click(object sender, EventArgs e) { StartThread(); }
Para implementar um botão Cancelar que para o encadeamento
Chame o procedimento StopThread do manipulador de eventos Click para o botão Cancel.
Private Sub Cancel_Click() Handles Cancel.Click ' Cancel the asynchronous operation. Me.BackgroundWorker1.CancelAsync() End Sub
private void Cancel_Click(object sender, EventArgs e) { // Cancel the asynchronous operation. this.backgroundWorker1.CancelAsync(); }
Testando
Agora você pode testar o aplicativo para certificar-se de que ele funciona corretamente.
Para testar o aplicativo
Pressione F5 para executar o aplicativo.
Quando o formulário é mostrado, entre o caminho do arquivo para o arquivo que você deseja testar na caixa sourceFile. Por exemplo, assumindo que seu arquivo de teste é chamado de Test.txt, entre C:\Test.txt.
Na segunda caixa de texto, entre uma palavra ou frase para a aplicação procurar no arquivo texto.
Clique no botão Start. O botão LinesCounted deve começar a incrementar imediatamente. A aplicação mostra a mensagem "Finished Counting" quando acaba.
Para testar o botão Cancelar
Pressione F5 para iniciar a aplicação, e entre o nome do arquivo e procure uma palavra como descrito no procedimento anterior. Certifique que o arquivo que você escolheu é grande o suficiente para garantir que você terá tempo de cancelar o procedimento antes que ele termine.
Clique no botão Start para iniciar a aplicação.
Clique no botão Cancel. A aplicação deve parar de contar imediatamente.
Próximas etapas
Essa aplicação contém alguma manipulação de erros básica. Ela detecta strings de pesquisa em branco. Você pode fazer esse programa mais robusto manipulando outros erros, como exceder o número de palavras ou linhas que podem ser contados.
Consulte também
Tarefas
Instruções passo a passo: criando um componente multithreaded simples com o Visual Basic
Como realizar e cancelar a assinatura de eventos (Guia de Programação em C#)