Demonstra Passo a passo: Criação de um componente Multithreaded simples com o Visual Basic
The BackgroundWorker componente substitui e acrescenta funcionalidade para o System.Threading espaço para nome; Entretanto, o System.Threading espaço para nome é mantido para compatibilidade com versões anteriores e para uso futuro, se você escolher. Para obter mais informações, consulte BackgroundWorker componente Overview.
Você pode escrever aplicativos que são capazes de realizar várias tarefas simultaneamente.Essa capacidade, chamada Multithreading, or disponível de threading, é uma maneira poderosa de componentes de design intensivo de processador e solicitar a entrada do usuário.Um exemplo de um componente que pode fazer uso de multithreading seria um componente que calcula informações de folha de pagamento.O componente pode processar dados inseridos em um banco de dados por um usuário em um segmento enquanto intensivo de processador da folha de pagamento cálculos foram efetuados em outro.Executando esses processos em threads separados, os usuários não são necessário aguardar que o computador para concluir cálculos antes de inserir dados adicionais.Nesta explicação passo a passo, você criará um componente multithreaded simples que executa vários cálculos complexos simultaneamente.
Criando o projeto
Seu aplicativo consistirá em um único formulário e um componente.O usuário entrará com valores e o sinal para o componente para iniciar os cálculos.O formulário, em seguida, irá receber valores de seu componente e exibi-los em controles de rótulo.O componente será executar cálculos intensivo de processador e o formulário ao final do sinal.Você irá criar variáveis públicas em seu componente para armazenar valores recebidos de sua interface do usuário.Você também será implementar métodos em seu componente para realizar cálculos com base nos valores dessas variáveis.
Observação: |
---|
Enquanto uma função é geralmente preferível para um método que calcula um valor, não é possível passar argumentos entre threads, nem podem ser valores retornados.Há várias maneiras simples de fornecer valores para segmentos e para receber valores deles.Nesta demonstração, você retornará valores à interface do usuário, atualizando variáveis públicas e eventos serão usados para notificar o programa principal quando um thread concluiu a execução. As caixas de diálogo e comandos de menu demonstradas podem ser diferentes daqueles descritas na Ajuda, dependendo das configurações ativas ou configurações de edição.Para alterar as configurações, escolher Importar e exportar configurações sobre o Ferramentas menu.Para obter mais informações, consulte Configurações do Visual Studio . |
Para criar o formulário
Criar um novo projeto Windows Application.
Nomeie o aplicativo Calculations e renomear Form1.vb sistema autônomo frmCalculations.vb.
Quando o Visual Studio solicita que você renomeie o Form1 elemento de código, clicar Sim.
Este formulário servirá sistema autônomo a interface primária usuário para seu aplicativo.
Adicionar cinco Label controles, quatro Button controles e um TextBox controle ao formulário.
Controle
Nome
Texto
Label1
lblFactorial1
(vazio)
Label2
lblFactorial2
(vazio)
Label3
lblAddTwo
(vazio)
Label4
lblRunLoops
(vazio)
Label5
lblTotalCalculations
(vazio)
Button1
btnFactorial1
Fatorial
Button2
btnFactorial2
Fatorial - 1
Button3
btnAddTwo
Adicionar dois
Button4
btnRunLoops
Executar um loop
TextBox1
txtValue
(vazio)
Para criar o componente de Calculadora
From a Projeto menu, selecionar Adicionar componente.
Nomeie esse componente Calculator.
Para adicionar variáveis públicas para o componente de Calculadora
Abra o Editor de código for Calculator.
Adicione instruções para criar variáveis públicas que será usado para passar valores de frmCalculations a cada thread.
A variável varTotalCalculations manter um total acumulado do número total de cálculos executado por componente e outras variáveis serão receberão valores do 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 ao componente de Calculadora
Declare os eventos que seu componente usará para se comunicar de valores de seu formulário.Imediatamente abaixo do variável declarações inseridas na etapa anterior, digite o seguinte código:
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)
Imediatamente abaixo de declarações de variáveis inseridas na etapa 1, digite o seguinte código:
' 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
Transferência de entrada de usuário para o componente
A próxima etapa é adicionar código ao frmCalculations para receber entrada do usuário e transferência e receber valores e dos Calculator componente.
Para implementar a funcionalidade de front-participante para frmCalculations
selecionar Criar solução from the Compilação menu.
Abra frmCalculations no Windows Forms Designer.
localizar o Componentes de cálculos guia o Caixa de ferramentas.arrastar um Calculadora componente para a área de design.
No Propriedades janelas, clicar no Eventos botão.
clicar duas vezes em cada uma a quatro evento s para criar evento manipuladores em frmCalculations. Você precisará retornar para o designer depois de cada manipulador de eventos é criado.
Insira o seguinte código para manipular os eventos que o 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
localizar o End Class demonstrativo na parte inferior da Editor de código.Imediatamente acima dela, adicione o seguinte código para lidar com cliques de botão:
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
Teste seu aplicativo
Agora você criou um projeto que incorpora um formulário e um componente capaz de executar vários cálculos complexos.Embora você não implementou multithreading capacidade ainda, você irá teste seu projeto para verificar sua funcionalidade antes de prosseguir.
Para testar seu projeto
From a Depurar menu, escolher Iniciar a depuração.O aplicativo começa e frmCalculations é exibida.
Na caixa de texto, digite 4, em seguida, clicar no botão rotulado como Adicionar dois.
O número "6" deve ser exibido na legenda abaixo dela e "Total de cálculos são 1" deve ser exibido nolblTotalCalculations.
Agora, clicar no botão rotulado como Fatorial - 1.
O número "6" deve ser exibido abaixo do botão, elblTotalCalculations agora diz "Total de cálculos são 4."
alterar o valor na caixa de texto para 20, em seguida, clicar no botão rotulado como Fatorial.
O número "2.43290200817664E + 18" é exibido abaixo dela, elblTotalCalculations agora diz "Total de cálculos são 24."
alterar o valor na caixa de texto para 50000e, em seguida, clicar no botão rotulado como Executar um loop.
Observe que há um intervalo pequeno, mas perceptível antes que este botão seja reativado.O rótulo sob este botão deve ler "50000" e os cálculos total exibidos são "25000024."
Altere o valor na caixa de texto para 5000000 e clicar no botão rotulado Executar um loope, em seguida, clicar imediatamente o botão rotulado como Adicionar dois.clicar Adicionar dois novamente.
O botão não responde, nem qualquer controle no formulário irá responder até que os loops tem sido completados.
Se seu programa for executado em um único thread de execução, cálculos intensivo de processador sistema autônomo, por exemplo, o exemplo acima têm a tendência a ocupar o programa até que sistema autônomo cálculos tenham sido concluídos.A próxima seção, você irá adicionar capacidade multithreading ao seu aplicativo para que vários threads podem ser executados ao mesmo tempo.
Adicionando capacidade multithreading
O exemplo anterior demonstrou as limitações de aplicativos executados somente um único segmento de execução.Na próxima seção, você usará o Thread classe para adicionar vários threads de execução ao seu componente.
Para adicionar a sub-rotina de segmentos
em aberto calculadora.vb in the Editor de código.Na parte superior do código, localizar o Public Class Calculator linha. Imediatamente abaixo dela, 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 do End Class demonstrativo na parte inferior do 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 Thread objeto é instanciado, ele exige um argumento na forma de um ThreadStart objeto. The ThreadStart o objeto é um delegado que aponta para o endereço da sub-rotina onde o thread está para começar. A ThreadStart objeto não pode receber parâmetros ou valores de passagem e, portanto, não pode indicar uma função. The Operador AddressOf Retorna um delegado que serve sistema autônomo o ThreadStart objeto. The ChooseThreads sub-rotina implementadas apenas receberá um valor do programa chamá-lo e usar esse valor para determinar o apropriado thread para iniciar.
Para adicionar o código de iniciar o thread para frmCalculations
Abra o frmCalculations.vb arquivo a Editor de código.localizar Sub btnFactorial1_Click.
Comment out the line that calls the Calculator1.Factorialmethod directly as shown:
' Calculator1.Factorial
Adicione a seguinte linha para telefonar o Calculator1.ChooseThreads método:
' Passes the value 1 to Calculator1, thus directing it to start the ' correct thread. Calculator1.ChooseThreads(1)
Faça modificações semelhantes a outros button_click sub-rotinas.
Observação: Certifique-se de incluir o valor apropriado para o threads argumento.
Quando terminar, seu código deve parecer semelhante à 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
marshaling de chamadas para controles
Agora facilitará atualizando a exibição no formulário.Como controles sempre são de propriedade o principal segmento de execução, qualquer telefonar para um controle de um thread subordinada requer um marshaling telefonar.O marshaling é o ato de mover uma telefonar entre thread limites e é muito caro em termos de recursos.Para minimizar a quantidade de marshaling que precisa ocorrer e certifique-se de que as chamadas são manipuladas de uma forma thread-safe, você usará o BeginInvoke para chamar métodos no thread principal de execução, minimizando assim a quantidade de cross-thread-limite de empacotamento que deve ocorrer. Esse tipo de telefonar é necessário ao chamar métodos que manipulam controles.Para obter mais informações, consulte Como: Manipular controles a partir de threads.
Para criar os procedimentos de invocar o controle
Abra o Editor de código for frmCalculations. Na seção declarações, 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 exigir um delegado para o método apropriado sistema autônomo um argumento. Essas linhas declaram o delegado assinaturas 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
From a edição menu, use Recortar and Colar para recortar todo o código de Sub Calculator1_FactorialComplete e colá-lo em FactHandler.
Repetir a etapa anterior para Calculator1_FactorialMinusComplete e Fact1Handler, Calculator1_AddTwoComplete e Add2Handler, e Calculator1_LoopComplete e LDoneHandler.
Quando terminar, não deve haver nenhum código restantes em Calculator1_FactorialComplete, Calculator1_FactorialMinusComplete, Calculator1_AddTwoComplete, e Calculator1_LoopCompletee todos os códigos esses usado para conter devem ter sido movidos para os novos métodos apropriados.
telefonar the BeginInvoke método para chamar os métodos de forma assíncrona. Você pode chamar BeginInvoke um (o formuláriome) ou qualquer um 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 sistema autônomo meio de evento manipulador é simplesmente fazer uma telefonar para o próximo método.O manipulador de eventos, na verdade, está causando um método ser chamado no thread principal de operação.Essa abordagem economiza em chamadas entre os limites do segmento e permite que seus aplicativos multissegmentados executar com eficiência e sem o receio de causar o travamento.Para obter detalhes sobre como trabalhar com controles em um ambiente multithreaded, consulte Como: Manipular controles a partir de threads.
salvar seu trabalho.
Testar sua solução, escolhendo Iniciar a depuração from the Depurar menu.
Tipo de 10000000 no texto e clicar Executar um loop.
"Repetição" é exibido no rótulo abaixo deste botão.Esse loop deve tomar muito time para executar.Se ele for concluído muito cedo, ajuste o dimensionar do número de acordo.
Na rápida sucessão, clicar todos os três botões que ainda estão ativados.Você descobrirá que todos os botões respondam à sua entrada.O rótulo abaixo Adicionar dois deve ser o primeiro para exibir um resultado.Os resultados mais tarde serão exibidos nos rótulos sob os botões fatorial.Avaliam esses resultados ao infinito, sistema autônomo o número retornado por um fatorial 10,000,000 é muito grande para uma variável de precisão dupla conter.Finalmente, após um intervalo adicional, os resultados são retornados sob o Executar um loop botão.
sistema autônomo apenas observadas, quatro conjuntos separados de cálculos foram realizados simultaneamente com quatro segmentos separados.A interface do usuário permanecia responsiva de entrada e foram retornados após cada thread concluída.
Coordenar os threads
Um usuário experiente do aplicativos multissegmentados pode percebem uma falha com o código sutil sistema autônomo digitado.Lembre-se as linhas de código de cada sub-rotina executar cálculo em Calculator:
varTotalCalculations += 1
varTotalAsOfNow = varTotalCalculations
Estas duas linhas de código incrementam a variável públicavarTotalCalculations e defina a variável local varTotalAsOfNow como esse valor. Esse valor é retornado ao frmCalculations e exibida em um controle rótulo. Mas está sendo retornado o valor correto?Se um único thread de execução está sendo executado, a resposta é Sim.Mas se estiver executando vários threads, a resposta se torna mais indefinida.Cada thread é capaz de incrementar a variável varTotalCalculations. É possível que essa variável é incrementado após um segmento, mas antes ele copia o valor para varTotalAsOfNow, outro thread podia alterar o valor dessa variável aumentando-lo. Isso leva à possibilidade de que cada thread está, na verdade, relatando resultados imprecisos.O Visual Basic fornece o Instrução SyncLock para permitir a sincronização de threads para garantir que cada thread sempre retorna um resultado preciso. A sintaxe de SyncLock é sistema autônomo segue:
SyncLock AnObject
Insert code that affects the object
Insert some more
Insert even more
' Release the lock
End SyncLock
Quando o SyncLock bloco é inserido, execução sobre a expressão especificada é bloqueada até que o segmento especificado tem um bloquear exclusivo no objeto em questão. No exemplo mostrado acima, a execução está bloqueada no AnObject. SyncLock deve ser usado com um objeto que retorna uma referência em vez de um valor. Em seguida, pode continuar a execução sistema autônomo um bloco sem interferência de outros segmentos.Um conjunto de instruções executadas sistema autônomo uma unidade é considerada atômica.Quando End SyncLock é encontrado, a expressão é liberada e os threads têm permissão para prossiga normalmente.
Para adicionar a demonstrativo SyncLock para seu aplicativo
em aberto calculadora.vb in the Editor de código.
localizar cada instância de código a seguir:
varTotalCalculations += 1 varTotalAsOfNow = varTotalCalculations
Deve haver quatro instâncias desse código, um em cada método de cálculo.
Modificar este código para que ele lê sistema autônomo segue:
SyncLock Me varTotalCalculations += 1 varTotalAsOfNow = varTotalCalculations End SyncLock
salvar seu trabalho e testá-la sistema autônomo no exemplo anterior.
Você pode perceber um pequeno impacto no desempenho do seu programa.Isso ocorre porque a execução de segmentos pára quando um bloquear exclusivo é obtido no seu componente.Embora ele garante a exatidão, essa abordagem impede alguns do benefício de desempenho de vários segmentos.Você deve cuidadosamente considere a necessidade de threads de bloqueio e implementá-las somente quando absolutamente necessário.
Consulte também
Tarefas
Como: Coordenar vários threads de execução
Conceitos
Com base em eventos Asynchronous Padrão Overview