Demonstra Passo a passo: Criação de um componente Multithreaded simples com translation from VPE for Csharp Visual
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: |
---|
Embora uma função geralmente é preferível para um método que calcula um valor, não é possível passar argumentos entre threads, nem valores podem ser 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.cs sistema autônomo frmCalculations.cs. Quando Visual Studio solicita que você renomeie a 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.
Defina propriedades para esses controles da seguinte maneira:
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.
Nome do componente de 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 int varAddTwo; public int varFact1; public int varFact2; public int varLoopValue; public double varTotalCalculations = 0;
Para adicionar métodos e eventos ao componente de Calculadora
Declare delegados para os eventos que seu componente usará para se comunicar valores de seu formulário.
Observação: Embora irá declarar quatro eventos, você só precisará criar três delegados, porque dois eventos terão a mesma assinatura.
Imediatamente abaixo do variável declarações inseridas na etapa anterior, digite o seguinte código:
// This delegate will be invoked with two of your events. public delegate void FactorialCompleteHandler(double Factorial, double TotalCalculations); public delegate void AddTwoCompleteHandler(int Result, double TotalCalculations); public delegate void LoopCompleteHandler(double TotalCalculations, int Counter);
Declare os eventos que seu componente usará para se comunicar com o seu aplicativo.Faça isso adicionando o código a seguir imediatamente abaixo o código digitado na etapa anterior.
public event FactorialCompleteHandler FactorialComplete; public event FactorialCompleteHandler FactorialMinusOneComplete; public event AddTwoCompleteHandler AddTwoComplete; public event LoopCompleteHandler LoopComplete;
Imediatamente abaixo do código digitado na etapa anterior, digite o seguinte código:
// This method will calculate the value of a number minus 1 factorial // (varFact2-1!). public void FactorialMinusOne() { double varTotalAsOfNow = 0; double varResult = 1; // Performs a factorial calculation on varFact2 - 1. for (int varX = 1; varX <= varFact2 - 1; varX++) { varResult *= varX; // Increments varTotalCalculations and keeps track of the current // total as of this instant. varTotalCalculations += 1; varTotalAsOfNow = varTotalCalculations; } // Signals that the method has completed, and communicates the // result and a value of total calculations performed up to this // point. FactorialMinusOneComplete(varResult, varTotalAsOfNow); } // This method will calculate the value of a number factorial. // (varFact1!) public void Factorial() { double varResult = 1; double varTotalAsOfNow = 0; for (int varX = 1; varX <= varFact1; varX++) { varResult *= varX; varTotalCalculations += 1; varTotalAsOfNow = varTotalCalculations; } FactorialComplete(varResult, varTotalAsOfNow); } // This method will add two to a number (varAddTwo+2). public void AddTwo() { double varTotalAsOfNow = 0; int varResult = varAddTwo + 2; varTotalCalculations += 1; varTotalAsOfNow = varTotalCalculations; AddTwoComplete(varResult, varTotalAsOfNow); } // This method will run a loop with a nested loop varLoopValue times. public void RunALoop() { int varX; double varTotalAsOfNow = 0; for (varX = 1; varX <= varLoopValue; varX++) { // This nested loop is added solely for the purpose of slowing down // the program and creating a processor-intensive application. for (int varY = 1; varY <= 500; varY++) { varTotalCalculations += 1; varTotalAsOfNow = varTotalCalculations; } } LoopComplete(varTotalAsOfNow, varLoopValue); }
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
em aberto frmCalculations no Editor de código.
localizar o public partial class frmCalculations demonstrativo. Imediatamente abaixo o { tipo:
Calculator Calculator1;
localizar o construtor.Imediatamente antes do }, adicione a seguinte linha:
// Creates a new instance of Calculator. Calculator1 = new Calculator();
No designer, clicar cada botão para gerar a estrutura de tópicos de código para Click evento manipuladores e adicione código para criar os manipuladores.
Quando concluir, seu Click manipuladores de eventos devem ser semelhante ao seguinte:
// Passes the value typed in the txtValue to Calculator.varFact1. private void btnFactorial1_Click(object sender, System.EventArgs e) { Calculator1.varFact1 = int.Parse(txtValue.Text); // Disables the btnFactorial1 until this calculation is complete. btnFactorial1.Enabled = false; Calculator1.Factorial(); } private void btnFactorial2_Click(object sender, System.EventArgs e) { Calculator1.varFact2 = int.Parse(txtValue.Text); btnFactorial2.Enabled = false; Calculator1.FactorialMinusOne(); } private void btnAddTwo_Click(object sender, System.EventArgs e) { Calculator1.varAddTwo = int.Parse(txtValue.Text); btnAddTwo.Enabled = false; Calculator1.AddTwo(); } private void btnRunLoops_Click(object sender, System.EventArgs e) { Calculator1.varLoopValue = int.Parse(txtValue.Text); btnRunLoops.Enabled = false; // Lets the user know that a loop is running lblRunLoops.Text = "Looping"; Calculator1.RunALoop(); }
Após o código adicionado na etapa anterior, digite o seguinte para manipular os eventos que o formulário receberá de Calculator1:
private void FactorialHandler(double Value, double Calculations) // Displays the returned value in the appropriate label. { lblFactorial1.Text = Value.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 " + Calculations.ToString(); } private void FactorialMinusHandler(double Value, double Calculations) { lblFactorial2.Text = Value.ToString(); btnFactorial2.Enabled = true; lblTotalCalculations.Text = "TotalCalculations are " + Calculations.ToString(); } private void AddTwoHandler(int Value, double Calculations) { lblAddTwo.Text = Value.ToString(); btnAddTwo.Enabled = true; lblTotalCalculations.Text = "TotalCalculations are " + Calculations.ToString(); } private void LoopDoneHandler(double Calculations, int Count) { btnRunLoops.Enabled = true; lblRunLoops.Text = Count.ToString(); lblTotalCalculations.Text = "TotalCalculations are " + Calculations.ToString(); }
No construtor de frmCalculations, adicione o seguinte código imediatamente antes de } para manipular os eventos personalizados do formulário receberá do Calculator1.
Calculator1.FactorialComplete += new Calculator.FactorialCompleteHandler(this.FactorialHandler); Calculator1.FactorialMinusOneComplete += new Calculator.FactorialCompleteHandler(this.FactorialMinusHandler); Calculator1.AddTwoComplete += new Calculator.AddTwoCompleteHandler(this.AddTwoHandler); Calculator1.LoopComplete += new Calculator.LoopCompleteHandler(this.LoopDoneHandler);
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ê testará 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, elblTotalCalculationsAgora lê "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, elblTotalCalculationsAgora lê "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 loop, em seguida, clicar imediatamente no botão rotulado como Adicionar dois.clicar 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.cs in the Editor de código.
Na parte superior do código, localizar a declaração de classe e imediatamente abaixo o {, digite o seguinte:
// Declares the variables you will use to hold your thread objects. public System.Threading.Thread FactorialThread; public System.Threading.Thread FactorialMinusOneThread; public System.Threading.Thread AddTwoThread; public System.Threading.Thread LoopThread;
Imediatamente antes do participante da declaração da classe na parte inferior do código, adicione o seguinte método:
public void ChooseThreads(int threadNumber) { // Determines which thread to start based on the value it receives. switch(threadNumber) { case 1: // Sets the thread using the AddressOf the subroutine where // the thread will start. FactorialThread = new System.Threading.Thread(new System.Threading.ThreadStart(this.Factorial)); // Starts the thread. FactorialThread.Start(); break; case 2: FactorialMinusOneThread = new System.Threading.Thread(new System.Threading.ThreadStart(this.FactorialMinusOne)); FactorialMinusOneThread.Start(); break; case 3: AddTwoThread = new System.Threading.Thread(new System.Threading.ThreadStart(this.AddTwo)); AddTwoThread.Start(); break; case 4: LoopThread = new System.Threading.Thread(new System.Threading.ThreadStart(this.RunALoop)); LoopThread.Start(); break; } }
Quando um Thread é instanciado, ele exige um argumento na forma de um ThreadStart. The ThreadStart é um delegado que aponta para o endereço do método em que o thread está para começar. A ThreadStart não é possível tirar parâmetros ou valores de passagem e, portanto, só pode indicar um void método. The ChooseThreads método que é implementado apenas receberá um valor do programa chamá-lo e usar esse valor para determinar o segmento apropriado para iniciar.
Para adicionar o código apropriado para frmCalculations
Abra o frmCalculations.cs arquivo a Editor de código localizarprivate void btnFactorial1_Click.
Comente a linha que chama o Calculator1.Factorial1 método diretamente, sistema autônomo mostrado:
// 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 métodos.
Observação: Certifique-se de incluir o valor apropriado para o Threads argumento.
Quando terminar, seu código deve parecer semelhante à seguinte:
private void btnFactorial1_Click(object sender, System.EventArgs e) // Passes the value typed in the txtValue to Calculator.varFact1 { Calculator1.varFact1 = int.Parse(txtValue.Text); // Disables the btnFactorial1 until this calculation is complete btnFactorial1.Enabled = false; // Calculator1.Factorial(); Calculator1.ChooseThreads(1); } private void btnFactorial2_Click(object sender, System.EventArgs e) { Calculator1.varFact2 = int.Parse(txtValue.Text); btnFactorial2.Enabled = false; // Calculator1.FactorialMinusOne(); Calculator1.ChooseThreads(2); } private void btnAddTwo_Click(object sender, System.EventArgs e) { Calculator1.varAddTwo = int.Parse(txtValue.Text); btnAddTwo.Enabled = false; // Calculator1.AddTwo(); Calculator1.ChooseThreads(3); } private void btnRunLoops_Click(object sender, System.EventArgs e) { Calculator1.varLoopValue = int.Parse(txtValue.Text); btnRunLoops.Enabled = false; // Lets the user know that a loop is running lblRunLoops.Text = "Looping"; // Calculator1.RunALoop(); Calculator1.ChooseThreads(4); }
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 Control.BeginInvoke método para invocar métodos no thread principal de execução, minimizando assim a quantidade de empacotamento de limite de thread de cruz que deve ocorrer. Esse tipo de telefonar é necessário ao chamar métodos que manipulam controles.Para obter detalhes, consulte:Como: Manipular controles a partir de threads.
Para criar os procedimentos de invocar o controle
em em aberto o editor de códigos para frmCalculations. Na seção declarações, adicione o seguinte código:
public delegate void FHandler(double Value, double Calculations); public delegate void A2Handler(int Value, double Calculations); public delegate void LDHandler(double Calculations, int Count);
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 void FactHandler(double Value, double Calculations) { } public void Fact1Handler(double Value, double Calculations) { } public void Add2Handler(int Value, double Calculations) { } public void LDoneHandler(double Calculations, int Count) { }
From a edição menu, use Recortar and Colar para recortar todo o código do método FactorialHandler e colá-lo em FactHandler.
Repetir a etapa anterior para FactorialMinusHandler e Fact1Handler, AddTwoHandler e Add2Handler, e LoopDoneHandler e LDoneHandler.
Quando terminar, não deve haver nenhum código restantes em FactorialHandler, Factorial1Handler, AddTwoHandler, e LoopDoneHandlere 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áriothis) ou qualquer um dos controles no formulário.
Ao concluir, seu código deve parecer semelhante à seguinte:
protected void FactorialHandler(double Value, double Calculations) { // 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. this.BeginInvoke(new FHandler(FactHandler), new Object[] {Value, Calculations}); } protected void FactorialMinusHandler(double Value, double Calculations) { this.BeginInvoke(new FHandler(Fact1Handler), new Object [] {Value, Calculations}); } protected void AddTwoHandler(int Value, double Calculations) { this.BeginInvoke(new A2Handler(Add2Handler), new Object[] {Value, Calculations}); } protected void LoopDoneHandler(double Calculations, int Count) { this.BeginInvoke(new LDHandler(LDoneHandler), new Object[] {Calculations, Count}); }
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 método de execução de 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.Visual C# Fornece o bloquear instrução (Referência C#) para permitir a sincronização de threads para garantir que cada thread sempre retorna um resultado preciso. A sintaxe de lock é sistema autônomo segue:
lock(AnObject)
{
// Insert code that affects the object.
// Insert more code that affects the object.
// Insert more code that affects the object.
// Release the lock.
}
Quando o lock 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. lock 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 o } é encontrado, a expressão é liberada e os threads têm permissão para prossiga normalmente.
Para adicionar a demonstrativo bloquear ao seu aplicativo
em aberto calculadora.cs 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:
lock(this) { varTotalCalculations += 1; varTotalAsOfNow = varTotalCalculations; }
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 dos benefícios 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
Demonstra Passo a passo: Criação de um componente Multithreaded simples com o Visual Basic
Conceitos
Com base em eventos Asynchronous Padrão Overview