Passo a passo: Criando um componente com simples com Visual Basic
O componente de BackgroundWorker substitui e adiciona funcionalidade ao namespace de System.Threading ; no entanto, o namespace de System.Threading é mantida para compatibilidade com versões anteriores e o uso futuro, se você escolher.Para mais informações, consulte Visão geral sobre o componente BackgroundWorker.
Você pode escrever aplicativos que podem executar várias tarefas simultaneamente.Essa capacidade, multithreadingchamado, ou segmentação livre, são uma maneira poderosa para criar componentes que são de processamento intensivo e de exigir a entrada do usuário.Um exemplo de um componente que pode utilizar multithreading seria um componente que calcula informações de folha de pagamento.O componente pode processar os dados inseridos em um banco de dados por um usuário de um segmento quando os cálculos de processamento intensivo de folha de pagamento foram executados em outro.Executando esses processos em segmentos separados, os usuários não precise esperar o computador para concluir cálculos antes de inserir dados adicionais.Em essa explicação passo a passo, você criará um componente simples com que executa cálculos mais complexos simultaneamente.
Criando o projeto
Seu aplicativo consistirá em um único formulário e um componente.O usuário entrará com valores e sinal para o componente iniciar cálculos.O formulário em receberá valores de seu componente e exibir-os-&z em controles de rótulo.O componente irá executar cálculos de processamento intensivo e sinalizará o formulário quando completo.Você criará variáveis públicas no seu componente para manter valores recebidos da interface do usuário.Você também irá implementar métodos em seu componente para executar cálculos com base nos valores de essas variáveis.
Observação |
---|
Quando uma função geralmente é preferível para um método que calcula um valor, os argumentos não podem ser passados entre segmentos, nem podem os valores retornados.Há várias maneiras simples de fornecer valores para segmentos e de receber valores de eles.Em a demonstração, você valores de retorno para sua interface do usuário atualizando variáveis públicas, e eventos serão usados para notificar o programa principal quando um segmento concluiu a execução. As caixas de diálogo e comandos de menu você vê podem diferir daquelas descritas na ajuda dependendo de suas configurações ativas ou versão.Para alterar suas configurações, escolha Import and Export Settings no menu Tools.Para mais informações, consulte Configurações de Visual Studio. |
Para criar o formulário
Criar um novo projeto Windows Application.
Nomeie o aplicativo Calculations e renomear Form1.vb como frmCalculations.vb.
Quando o Visual Studio solicita a renomear o elemento de código de Form1 , clique Sim.
Este formulário servirá como a interface primária para seu aplicativo.
Adicione cinco controles de Label , quatro controles de Button , e um controle de TextBox ao seu formulário.
Controle
Nome
Texto
Label1
lblFactorial1
(em branco)
Label2
lblFactorial2
(em branco)
Label3
lblAddTwo
(em branco)
Label4
lblRunLoops
(em branco)
Label5
lblTotalCalculations
(em branco)
Button1
btnFactorial1
Factorial
Button2
btnFactorial2
Factorial - 1
Button3
btnAddTwo
Adicione dois
Button4
btnRunLoops
Executar um loop
TextBox1
txtValue
(em branco)
Para criar o componente da calculadora
Em o menu de Projeto , selecione Adicionar Componente.
Nomeie este Calculatorcomponente.
Para adicionar variáveis públicas para o componente da calculadora
Abra editor de códigos para Calculator.
Adicione instruções para criar variáveis públicas que você usará para passar valores de frmCalculations a cada segmento.
A variável manterá um total varTotalCalculations executando o número total de cálculos executados pelo componente, e os outros valores de variáveis receberá formulário.
Public varAddTwo As Integer Public varFact1 As Integer Public varFact2 As Integer Public varLoopValue As Integer Public varTotalCalculations As Double = 0
Para adicionar métodos e eventos para o componente da calculadora
Declarar eventos que seu componente usará para comunicar valores para o formulário.Logo abaixo das declarações de variáveis inseridas para a etapa anterior, digite o código a seguir:
Public Event FactorialComplete(ByVal Factorial As Double, ByVal _ TotalCalculations As Double) Public Event FactorialMinusComplete(ByVal Factorial As Double, ByVal _ TotalCalculations As Double) Public Event AddTwoComplete(ByVal Result As Integer, ByVal _ TotalCalculations As Double) Public Event LoopComplete(ByVal TotalCalculations As Double, ByVal _ Counter As Integer)
Logo abaixo das declarações de variável à etapa 1, digite o código a seguir:
' This sub will calculate the value of a number minus 1 factorial ' (varFact2-1!). Public Sub FactorialMinusOne() Dim varX As Integer = 1 Dim varTotalAsOfNow As Double Dim varResult As Double = 1 ' Performs a factorial calculation on varFact2 - 1. For varX = 1 to varFact2 - 1 varResult *= varX ' Increments varTotalCalculations and keeps track of the current ' total as of this instant. varTotalCalculations += 1 varTotalAsOfNow = varTotalCalculations Next varX ' Signals that the method has completed, and communicates the ' result and a value of total calculations performed up to this ' point RaiseEvent FactorialMinusComplete(varResult, varTotalAsOfNow) End Sub ' This sub will calculate the value of a number factorial (varFact1!). Public Sub Factorial() Dim varX As Integer = 1 Dim varResult As Double = 1 Dim varTotalAsOfNow As Double = 0 For varX = 1 to varFact1 varResult *= varX varTotalCalculations += 1 varTotalAsOfNow = varTotalCalculations Next varX RaiseEvent FactorialComplete(varResult, varTotalAsOfNow) End Sub ' This sub will add two to a number (varAddTwo + 2). Public Sub AddTwo() Dim varResult As Integer Dim varTotalAsOfNow As Double varResult = varAddTwo + 2 varTotalCalculations += 1 varTotalAsOfNow = varTotalCalculations RaiseEvent AddTwoComplete(varResult, varTotalAsOfNow) End Sub ' This method will run a loop with a nested loop varLoopValue times. Public Sub RunALoop() Dim varX As Integer Dim varY As Integer Dim varTotalAsOfNow As Double For varX = 1 To varLoopValue ' This nested loop is added solely for the purpose of slowing ' down the program and creating a processor-intensive ' application. For varY = 1 To 500 varTotalCalculations += 1 varTotalAsOfNow = varTotalCalculations Next Next RaiseEvent LoopComplete(varTotalAsOfNow, varX - 1) End Sub
Transferindo a entrada do usuário para o componente
A próxima etapa é adicionar código a frmCalculations para receber entrada do usuário e para transferir e receber valores de e o componente de Calculator .
Para implementar a funcionalidade front-end para frmCalculations
Compilar solução Selecione no menu de Compilar .
Abra frmCalculations no Windows Forms Designer.
Localize a guia de Componentes de cálculos em Caixa de Ferramentas.Arraste um componente de Calculadora na superfície de design.
Em o windows de Propriedades , clique no botão de Eventos .
Clique duas vezes em cada um dos quatro eventos para criar manipuladores de eventos em frmCalculations.Você precisará retornar ao designer depois de cada manipulador de eventos é criado.
Insira o seguinte código para manipular eventos que seu formulário receberá de Calculator1:
Private Sub Calculator1_AddTwoComplete(ByVal Result As System.Int32, ByVal TotalCalculations As System.Double) Handles Calculator1.AddTwoComplete lblAddTwo.Text = Result.ToString btnAddTwo.Enabled = True lblTotalCalculations.Text = "TotalCalculations are " & _ TotalCalculations.ToString End Sub Private Sub Calculator1_FactorialComplete(ByVal Factorial As System.Double, ByVal TotalCalculations As System.Double) Handles Calculator1.FactorialComplete ' Displays the returned value in the appropriate label. lblFactorial1.Text = Factorial.ToString ' Re-enables the button so it can be used again. btnFactorial1.Enabled = True ' Updates the label that displays the total calculations performed lblTotalCalculations.Text = "TotalCalculations are " & _ TotalCalculations.ToString End Sub Private Sub Calculator1_FactorialMinusComplete(ByVal Factorial As System.Double, ByVal TotalCalculations As System.Double) Handles Calculator1.FactorialMinusComplete lblFactorial2.Text = Factorial.ToString btnFactorial2.Enabled = True lblTotalCalculations.Text = "TotalCalculations are " & _ TotalCalculations.ToString End Sub Private Sub Calculator1_LoopComplete(ByVal TotalCalculations As System.Double, ByVal Counter As System.Int32) Handles Calculator1.LoopComplete btnRunLoops.Enabled = True lblRunLoops.Text = Counter.ToString lblTotalCalculations.Text = "TotalCalculations are " & _ TotalCalculations.ToString End Sub
Localize a declaração de End Class na parte inferior de editor de códigos.Imediatamente acima, adicione o seguinte código à cliques de botão de forma:
Private Sub btnFactorial1_Click(ByVal sender As Object, ByVal e As _ System.EventArgs) Handles btnFactorial1.Click ' Passes the value typed in the txtValue to Calculator.varFact1. Calculator1.varFact1 = CInt(txtValue.Text) ' Disables the btnFactorial1 until this calculation is complete. btnFactorial1.Enabled = False Calculator1.Factorial() End Sub Private Sub btnFactorial2_Click(ByVal sender As Object, ByVal e _ As System.EventArgs) Handles btnFactorial2.Click Calculator1.varFact2 = CInt(txtValue.Text) btnFactorial2.Enabled = False Calculator1.FactorialMinusOne() End Sub Private Sub btnAddTwo_Click(ByVal sender As Object, ByVal e As _ System.EventArgs) Handles btnAddTwo.Click Calculator1.varAddTwo = CInt(txtValue.Text) btnAddTwo.Enabled = False Calculator1.AddTwo() End Sub Private Sub btnRunLoops_Click(ByVal sender As Object, ByVal e As _ System.EventArgs) Handles btnRunLoops.Click Calculator1.varLoopValue = CInt(txtValue.Text) btnRunLoops.Enabled = False ' Lets the user know that a loop is running. lblRunLoops.Text = "Looping" Calculator1.RunALoop() End Sub
Testando o aplicativo
Você criou um projeto que incorpora um formulário e um componente capazes de vários executar cálculos complexos.Embora você não tiver implementado o recurso de multithreading ainda, você irá testar seu projeto para verificar sua funcionalidade antes de continuar.
Para testar o projeto
Em o menu de Depurar , escolha Iniciar Depuração.O aplicativo é iniciado e frmCalculations aparece.
Em a caixa de texto, digite 4, clique no botão rotulado Adicione dois.
O número “6 " deve ser exibido no rótulo abaixo de ele, e “os cálculos total são 1 " devem ser exibidos em lblTotalCalculations.
Agora clique no botão rotulado Factorial - 1.
O número “6 " deve ser exibido abaixo do botão, e os cálculos gerais de leitura de lblTotalCalculations agora “é 4. "
Altere o valor na caixa de texto a 20, clique no botão rotulado Fatorial.
O número 2.43290200817664E+18 “” é exibido abaixo de ele, e lê “cálculos gerais de lblTotalCalculations agora é 24.”
Altere o valor na caixa de texto a 50000, e clique no botão rotulado executar um loop.
Observe que há um pequeno intervalo mas visível antes que esse botão novamente está ativado.O rótulo em esse botão deve ler “50000 " e os cálculos gerais são exibidos “25000024”.
Altere o valor na caixa de texto a 5000000 e clique no botão rotulado executar um loop, e clique imediatamente no botão rotulado Adicione dois.Clique Adicione dois novamente.
O botão não responde, ou qualquer controle no formulário responderá até que os loops estejam concluídos.
Se seu programa é apenas um único thread de execução, os cálculos de processamento intensivo como o exemplo anterior têm a tendência a anterior o programa até que os cálculos estejam concluídos.Em a próxima seção, você irá adicionar o recurso de multithreading ao seu aplicativo para que vários segmentos podem ser executados imediatamente.
Adicionando recurso de multithreading
O exemplo anterior demonstrou as limitações de aplicativos que executam somente um único thread de execução.Em a próxima seção você usará a classe de Thread para adicionar vários segmentos de execução para seu componente.
Para adicionar a sub-rotina de segmentos
Calculator.vb Aberto em editor de códigos.Próximo à parte superior do código, localize a linha de Public Class Calculator.Logo abaixo de ele, digite o seguinte:
' Declares the variables you will use to hold your thread objects. Public FactorialThread As System.Threading.Thread Public FactorialMinusOneThread As System.Threading.Thread Public AddTwoThread As System.Threading.Thread Public LoopThread As System.Threading.Thread
Imediatamente antes da instrução de End Class na parte inferior de código, adicione o seguinte método:
Public Sub ChooseThreads(ByVal threadNumber As Integer) ' Determines which thread to start based on the value it receives. Select Case threadNumber Case 1 ' Sets the thread using the AddressOf the subroutine where ' the thread will start. FactorialThread = New System.Threading.Thread(AddressOf _ Factorial) ' Starts the thread. FactorialThread.Start() Case 2 FactorialMinusOneThread = New _ System.Threading.Thread(AddressOf FactorialMinusOne) FactorialMinusOneThread.Start() Case 3 AddTwoThread = New System.Threading.Thread(AddressOf AddTwo) AddTwoThread.Start() Case 4 LoopThread = New System.Threading.Thread(AddressOf RunALoop) LoopThread.Start() End Select End Sub
Quando um objeto de Thread é instanciado, requer um argumento na forma de um objeto de ThreadStart .O objeto de ThreadStart é um delegado que aponta para o endereço de sub-rotina onde o segmento é iniciar.Um objeto de ThreadStart não pode ter parâmetros ou passar valores, e não pode indicar uma função.Operador AddressOf (Visual Basic) retorna um delegado que serve como os ThreadStart objetem.A sub-rotina que você implementou de ChooseThreads receberá apenas um valor de programa que chama o e usará esse valor para determinar o segmento iniciar apropriado.
Para adicionar código para iniciar com frmCalculations
Abra o arquivo de frmCalculations.vb em editor de códigos.Localize Sub btnFactorial1_Click.
Comente a linha que chama o métodode Calculator1.Factorialdiretamente como mostrado:
' Calculator1.Factorial
Adicione a seguinte linha para chamar o método de Calculator1.ChooseThreads :
' Passes the value 1 to Calculator1, thus directing it to start the ' correct thread. Calculator1.ChooseThreads(1)
Faça alterações semelhantes aos outros sub-rotina de button_click .
Observação Certifique-se de incluir o valor apropriado para o argumento de threads .
Quando você terminar, o código deve ser semelhante ao seguinte:
Private Sub btnFactorial1_Click(ByVal sender As Object, ByVal e As _ System.EventArgs) Handles btnFactorial1.Click ' Passes the value typed in the txtValue to Calculator.varFact1. Calculator1.varFact1 = CInt(txtValue.Text) ' Disables the btnFactorial1 until this calculation is complete. btnFactorial1.Enabled = False ' Calculator1.Factorial() ' Passes the value 1 to Calculator1, thus directing it to start the ' Correct thread. Calculator1.ChooseThreads(1) End Sub Private Sub btnFactorial2_Click(ByVal sender As Object, ByVal e As _ System.EventArgs) Handles btnFactorial2.Click Calculator1.varFact2 = CInt(txtValue.Text) btnFactorial2.Enabled = False ' Calculator1.FactorialMinusOne() Calculator1.ChooseThreads(2) End Sub Private Sub btnAddTwo_Click(ByVal sender As Object, ByVal e As _ System.EventArgs) Handles btnAddTwo.Click Calculator1.varAddTwo = CInt(txtValue.Text) btnAddTwo.Enabled = False ' Calculator1.AddTwo() Calculator1.ChooseThreads(3) End Sub Private Sub btnRunLoops_Click(ByVal sender As Object, ByVal e As _ System.EventArgs) Handles btnRunLoops.Click Calculator1.varLoopValue = CInt(txtValue.Text) btnRunLoops.Enabled = False ' Lets the user know that a loop is running. lblRunLoops.Text = "Looping" ' Calculator1.RunALoop() Calculator1.ChooseThreads(4) End Sub
Chamadas empacotamento a controles
Você agora facilitará atualizar a exibição no formulário.Como controles são sempre pertencentes pelo segmento principal de execução, qualquer chamada a um controle de um segmento subordinado requer uma chamada empacotamento .Empacotamento é o ato de mover uma chamada através dos limites de segmento, e é muito caro em termos de recursos.Para minimizar a quantidade de empacotamento que precisa ocorrer, e para garantir que seus chamadas são tratados de maneira segura, você usará BeginInvoke para chamar métodos no segmento principal de execução, minimizando de essa maneira a quantidade de empacotamento de cruz-thread- limite que deve ocorrer.Esse tipo de chamada é necessário para chamar os métodos que manipulam controles.Para mais informações, consulte Como: Manipular controles de segmentos.
Para criar os procedimentos de controle chamando
Abra editor de códigos para frmCalculations.Em as declarações, a seção consulte adicione o seguinte código.
Public Delegate Sub FHandler(ByVal Value As Double, ByVal _ Calculations As Double) Public Delegate Sub A2Handler(ByVal Value As Integer, ByVal _ Calculations As Double) Public Delegate Sub LDhandler(ByVal Calculations As Double, ByVal _ Count As Integer)
Invoke e BeginInvoke requerem um representante para o método apropriado como um argumento.Essas linhas declaram as assinaturas de representante que serão usadas por BeginInvoke para chamar os métodos apropriados.
Adicione os seguintes métodos vazios ao seu código.
Public Sub FactHandler(ByVal Factorial As Double, ByVal TotalCalculations As _ Double) End Sub Public Sub Fact1Handler(ByVal Factorial As Double, ByVal TotalCalculations As _ Double) End Sub Public Sub Add2Handler(ByVal Result As Integer, ByVal TotalCalculations As _ Double) End Sub Public Sub LDoneHandler(ByVal TotalCalculations As Double, ByVal Counter As _ Integer) End Sub
Em o menu de Editar , use Recortar e Colar para recortar todo o código de Sub Calculator1_FactorialCompletee a colagem em FactHandler.
Repita a etapa anterior para Calculator1_FactorialMinusComplete e Fact1Handler, Calculator1_AddTwoComplete e Add2Handler, e Calculator1_LoopComplete e LDoneHandler.
Quando concluir, não deve haver qualquer código que permanece em Calculator1_FactorialComplete, em Calculator1_FactorialMinusComplete, em Calculator1_AddTwoComplete, e em Calculator1_LoopComplete, e qualquer código esses usados para conter deve ter sido movido para novos métodos apropriados.
Chame o método de BeginInvoke para chamar os métodos de forma assíncrona.Você pode chamar BeginInvoke do formulário (me) ou qualquer dos controles no formulário:
Private Sub Calculator1_FactorialComplete(ByVal Factorial As System.Double, ByVal TotalCalculations As System.Double) Handles Calculator1.FactorialComplete ' BeginInvoke causes asynchronous execution to begin at the address ' specified by the delegate. Simply put, it transfers execution of ' this method back to the main thread. Any parameters required by ' the method contained at the delegate are wrapped in an object and ' passed. Me.BeginInvoke(New FHandler(AddressOf FactHandler), New Object() _ {Factorial, TotalCalculations }) End Sub Private Sub Calculator1_FactorialMinusComplete(ByVal Factorial As System.Double, ByVal TotalCalculations As System.Double) Handles Calculator1.FactorialMinusComplete Me.BeginInvoke(New FHandler(AddressOf Fact1Handler), New Object() _ { Factorial, TotalCalculations }) End Sub Private Sub Calculator1_AddTwoComplete(ByVal Result As System.Int32, ByVal TotalCalculations As System.Double) Handles Calculator1.AddTwoComplete Me.BeginInvoke(New A2Handler(AddressOf Add2Handler), New Object() _ { Result, TotalCalculations }) End Sub Private Sub Calculator1_LoopComplete(ByVal TotalCalculations As System.Double, ByVal Counter As System.Int32) Handles Calculator1.LoopComplete Me.BeginInvoke(New LDHandler(AddressOf Ldonehandler), New Object() _ { TotalCalculations, Counter }) End Sub
Pode parecer se o manipulador de eventos está fazendo simplesmente uma chamada para o método a seguir.O manipulador de eventos está causando realmente um método a ser chamado no segmento principal da operação.Essa abordagem salva em chamadas através dos limites de segmento e permite seus aplicativos multissegmentados executar com e sem causar medo do aprisionamento.Para obter detalhes sobre como trabalhar com controles em um ambiente de vários segmentos, consulte Como: Manipular controles de segmentos.
Salvar seu trabalho.
Testar sua solução Iniciar Depuração escolhendo no menu de Depurar .
Digite 10000000 na caixa de texto e clique executar um loop.
“Loop” é exibido no rótulo abaixo de esse botão.Este loop deve levar uma quantidade significativa de tempo para executar.Se for concluída muito cedo, ajuste o tamanho do número de acordo.
Em sucessão rápida, clique em todos os três botões que estão habilitados ainda.Você verá que todos os botões respondem a sua entrada.O rótulo abaixo de Adicione dois deve ser a primeira para exibir um resultado.Os resultados serão exibidos posteriormente nos rótulos abaixo dos botões factorial.Esses resultados avaliada como infinito, como o número retornado por um 10.000.000 factorial é muito grande para que uma variável de precisão dupla contém.Finalmente, após um atraso adicional, resultados é retornado abaixo do botão de executar um loop .
Como você observou apenas, quatro conjuntos separados de cálculos foram executados simultaneamente em cima de quatro segmentos separados.A interface do usuário permanece responde a entrada, e os resultados são retornados após cada thread foi concluída.
Coordenando seus segmentos
Um usuário experiente de aplicativos de vários segmentos pode perceber uma falha sutil com o código como digitado.Lembre as linhas de código de cada sub-rotina cálculo- executando em Calculator:
varTotalCalculations += 1
varTotalAsOfNow = varTotalCalculations
Essas duas linhas de código incrementam varTotalCalculations variável pública e definem a variável local varTotalAsOfNow para este valor.Esse valor é retornado na frmCalculations e exibido em um controle do rótulo.Mas o valor correto está sendo retornado?Se apenas um único thread de execução está sendo executado, a resposta é claramente sim.Mas se vários segmentos estão executando o, a resposta torna-se mais incerta.Cada segmento tem a capacidade de incremento varTotalCalculationsvariável.É possível que depois que um segmento incrementa essa variável, mas antes que copia o valor a varTotalAsOfNow, outro segmento pode alterar o valor de essa variável incrementando o.Isso resulta na possibilidade que cada segmento, na verdade, está relatando resultados imprecisas.Visual Basic fornece Instrução SyncLock para permitir que a sincronização de threads certifique-se de que cada segmento sempre retorna um resultado preciso.A sintaxe para SyncLock é o seguinte:
SyncLock AnObject
Insert code that affects the object
Insert some more
Insert even more
' Release the lock
End SyncLock
Quando o bloco de SyncLock for inserida, a execução na expressão especificada será bloqueada até que o segmento especificado tem um bloqueio exclusivo no objeto em questão.Em o exemplo mostrado acima, a execução é bloqueada em AnObject.SyncLock deve ser usado com um objeto que retorna uma referência em vez de um valor.A execução pode prosseguir como um bloco para sem interferência de outros segmentos.Um conjunto de declarações que executam como uma unidade. seria atomicQuando End SyncLock é localizado, a expressão é liberado e os segmentos são permitidos normalmente continuar.
Para adicionar a instrução SyncLock a seu aplicativo
Calculator.vb Aberto em editor de códigos.
Localize cada instância de código a seguir:
varTotalCalculations += 1 varTotalAsOfNow = varTotalCalculations
Deve haver quatro instâncias de esse código, uma em cada método calculate.
Modifique este código para que ele leia o seguinte:
SyncLock Me varTotalCalculations += 1 varTotalAsOfNow = varTotalCalculations End SyncLock
Salve seu trabalho e testá-lo como no exemplo anterior.
Você pode observar um leve impacto no desempenho do seu programa.Isso ocorre porque a execução de segmentos para quando um bloqueio exclusivo é obtido no seu componente.Embora verifique se a precisão, essa abordagem impede de algum benefício de desempenho de vários segmentos.Você deve considerar com cuidado a necessidade para bloquear segmentos, e implementá-los com somente quando necessário.
Consulte também
Tarefas
Como: Vários segmentos de coordenadas de execução
Passo a passo: Criando um componente com simples com o visual C#
Referência
Conceitos
Event-based Asynchronous Pattern Overview