Demonstra Passo a passo: Criação de um componente Multithreaded simples com Visual C#

O BackgroundWorker componente substitui e adiciona funcionalidade para o System.Threading namespace; No entanto, o System.Threading namespace é mantido para compatibilidade com versões anteriores e o uso futuro, se você escolher. Para obter mais informações, consulte Visão geral sobre o componente BackgroundWorker.

Você pode escrever aplicativos que são capazes de realizar várias tarefas simultaneamente. Essa capacidade, chamada multithreading, ou livre de threading, é uma maneira eficiente para design de componentes que fazem uso intenso do processador e exigem 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 os cálculos de folha de pagamento de uso intenso do processador foram realizados em outro. Executando esses processos em threads separados, os usuários não precisam esperar o computador concluir cálculos antes de inserir dados adicionais. Esta explicação passo a passo, você irá 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 digitará os valores e o sinal para o componente para iniciar os cálculos. O formulário, em seguida, irá receber os valores de seu componente e exibi-los em controles de rótulo. O componente irá executar os cálculos de uso intenso do processador e o formulário após a conclusão de sinal. Você irá criar variáveis públicas em seu componente para armazenar valores recebidos de interface do usuário. Você também irá implementar métodos em seu componente para executar os cálculos com base nos valores dessas variáveis.

ObservaçãoObservação

Embora uma função geralmente é preferível para um método que calcula um valor, argumentos não podem ser passados entre threads, nem valores podem ser retornados. Há várias maneiras simples de fornecer valores para threads e receber os valores deles. Nesta demonstração, retornará valores para a 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 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.

Para criar o formulário

  1. Criar um novo projeto Windows Application.

  2. Nomeie o aplicativo Calculations e renomeie Form1.cs como frmCalculations.cs. Quando Visual Studio solicita que você renomeie o Form1 o elemento de código, clique em Sim.

    Este formulário servirá como a interface de usuário principal para o seu aplicativo.

  3. Adicionar cinco Label controles, quatro Button controles e um TextBox o controle ao seu formulário.

  4. Defina propriedades para esses controles, como segue:

    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

  1. Do projeto menu, selecione Adicionar o componente.

  2. Nome do componente Calculator.

Para adicionar variáveis públicas para o componente de Calculadora

  1. Abrir o O Editor de código para Calculator.

  2. Adicione instruções para criar variáveis públicas que você usará para passar valores de frmCalculations para cada segmento.

    A variável varTotalCalculations manter um total contínuo do número total de cálculos realizado pelo 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 para o componente de Calculadora

  1. Declare delegados para os eventos que seu componente usará para se comunicar valores ao seu formulário.

    ObservaçãoObservação

    Embora você irá declarar quatro eventos, você só precisará criar três delegados, porque os dois eventos terão a mesma assinatura.

    Imediatamente abaixo de declarações de variáveis 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);
    
  2. Declare os eventos que seu componente usará para se comunicar com seu aplicativo. Faça isso adicionando o seguinte código 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;
    
  3. 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);
    }
    

A transferência de entrada do usuário para o componente.

A próxima etapa é adicionar código para frmCalculations para receber entrada do usuário e para transferir e receber os valores de e para o Calculator componente.

Para implementar a funcionalidade de front-end para frmCalculations

  1. Abrir frmCalculations na O Editor de código.

  2. Localize o public partial class frmCalculations instrução. Imediatamente abaixo do { tipo:

    Calculator Calculator1;
    
  3. Localize o construtor. Imediatamente antes do }, adicione a seguinte linha:

    // Creates a new instance of Calculator.
    Calculator1 = new Calculator();
    
  4. No designer, clique em cada botão para gerar a estrutura de tópicos para cada controle código Click manipuladores de eventos e adicione código para criar manipuladores.

    Ao concluir, seu Click manipuladores de eventos devem ter o seguinte aspecto:

    // 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();
    }
    
  5. Após o código adicionado na etapa anterior, digite o seguinte para manipular os eventos em seu formulário receberá do 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();
    }
    
  6. No construtor do frmCalculations, adicione o seguinte código imediatamente antes do } para manipular os eventos personalizados, o 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 realizar vários cálculos complexos. Embora você ainda não implementou o recurso de multithreading, você testará o projeto para verificar sua funcionalidade antes de prosseguir.

Para testar seu projeto.

  1. Do Debug menu, escolha Start Debugging.

    O aplicativo começa e frmCalculations aparece.

  2. Na caixa de texto, digite 4, em seguida, clique no botão Adicionar dois.

    O número "6" deve ser exibido no rótulo abaixo dela e "Total cálculos são de 1" devem ser exibidos em lblTotalCalculations.

  3. Agora, clique no botão fatorial - 1.

    O número "6" deve ser exibido abaixo do botão, e lblTotalCalculations agora lê "Os cálculos são 4".

  4. Alterar o valor na caixa de texto para 20, em seguida, clique no botão fatorial.

    O número "2.43290200817664E + 18" é exibido abaixo dela, e lblTotalCalculations agora lê "Os cálculos são 24".

  5. Alterar o valor na caixa de texto para 50000e em seguida, clique no botão Executar o Loop de um.

    Observe que há um intervalo pequeno, mas perceptível antes que este botão é reativado. O rótulo do botão deve ler "50000" e os cálculos total exibidos são "25000024".

  6. Alterar o valor na caixa de texto para 5000000 e clique no botão Executar Loop de um, em seguida, imediatamente, clique no botão Adicionar dois. Clique novamente.

    O botão não responde nem qualquer controle no formulário responderá até que os loops foram concluídos.

    Se o seu programa é executado em um único thread de execução, cálculos de uso intenso do processador como, por exemplo, o exemplo acima têm a tendência a ocupar o programa, até que os cálculos foram concluídos. Na próxima seção, você adicionará multithreading capacidade para seu aplicativo para que vários threads sejam executados ao mesmo tempo.

Adição da funcionalidade de Multithreading

O exemplo anterior demonstrou as limitações de aplicativos executados somente um único segmento de execução. A próxima seção, você usará o Thread classe para adicionar vários threads de execução para o seu componente.

Para adicionar a sub-rotina de Threads

  1. Abrir Calculator.cs no O Editor de código.

  2. Próximo à parte superior do código, localize a declaração de classe e imediatamente abaixo do {, 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;
    
  3. Imediatamente antes do fim 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 requer um argumento na forma de um ThreadStart. O ThreadStart é um delegado que aponta para o endereço do método onde o thread está começar. A ThreadStart não é possível obter parâmetros ou valores de passagem e, portanto, só pode indicar um void método. O ChooseThreads método acabou de ser implementado irá receber um valor a partir do programa chamá-lo e usar esse valor para determinar o segmento apropriado para iniciar.

Para adicionar o código apropriado para frmCalculations

  1. Abrir o frmCalculations.cs de arquivo no O Editor de código, localize private void btnFactorial1_Click.

    1. Comente a linha que chama o Calculator1.Factorial1 o método diretamente, como mostrado:

      // Calculator1.Factorial()
      
    2. Adicione a seguinte linha para chamar o Calculator1.ChooseThreads método:

      // Passes the value 1 to Calculator1, thus directing it to start the 
      // correct thread.
      Calculator1.ChooseThreads(1);
      
  2. Fazer modificações semelhantes a outros button_click métodos.

    ObservaçãoObservação

    Certifique-se de incluir o valor apropriado para o Threads argumento.

    Quando terminar, seu código deve parecer semelhante ao 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);
    }
    

Empacotamento de chamadas para controles

Agora você irá facilitar a atualização da exibição no formulário. Porque controles pertencem sempre o principal segmento de execução, qualquer chamada para um controle de um thread subordinada requer um empacotamento de chamada. O empacotamento é o ato de mover uma chamada através de limites de segmento e é muito caro em termos de recursos. Para minimizar a quantidade de empacotamento precisa ocorrer e certifique-se de que as chamadas são manipuladas de 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 cruzado que deve ocorrer. Esse tipo de chamada é necessário ao chamar métodos que manipulam os controles. Para obter detalhes, consulte:Como: Manipular controles de Threads.

Para criar os procedimentos de invocar o controle

  1. Abrir o editor de código 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);
    

    Invokee BeginInvoke requerem um delegado para o método apropriado como um argumento. Essas linhas declaram o delegado assinaturas que serão usadas por BeginInvoke para chamar os métodos apropriados.

  2. Adicione os seguintes métodos de vazios para o 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)
    {
    }
    
  3. Do Editar menu, use Recortar e Colar para recortar todo o código do método FactorialHandler e colá-lo em FactHandler.

  4. Repita 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 todo o código esses usado para conter devem foram movidos para o novo apropriada de métodos.

  5. Chamar o BeginInvoke método para invocar os métodos de forma assíncrona. Você pode chamar BeginInvoke partir de um formulário (this) ou qualquer um dos controles no formulário.

    Ao concluir, seu código deve parecer semelhante ao 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 como se o manipulador de eventos está fazendo simplesmente uma chamada para o próximo método. O manipulador de eventos, na verdade, está causando um método a ser invocado no thread principal da operação. Essa abordagem é salva em chamadas entre os limites de thread e permite que seus aplicativos multithread executar com eficiência e sem medo de causar o travamento. Para obter detalhes sobre como trabalhar com controles em um ambiente multithread, consulte Como: Manipular controles de Threads.

  6. Salve seu trabalho.

  7. Testar sua solução, escolhendo Start Debugging partir do Debug menu.

    1. Tipo de 10000000 na caixa de texto e clique Executar o Loop de um.

      "Repetição" é exibido no rótulo abaixo deste botão. Esse loop deve levar uma quantidade significativa de tempo para ser executado. Se ele for concluído muito cedo, ajuste o tamanho do número de acordo.

    2. Em rápida sucessão, clique em todos os três botões ainda estão habilitados. Você descobrirá que todos os botões respondem à entrada. O rótulo abaixo Adicionar dois deve ser o primeiro para exibir um resultado. Resultados serão exibidos posteriormente nos rótulos sob os botões fatorial. Esses resultados avaliam infinitamente, conforme o número retornado por uma fatorial 10.000.000 é muito grande para conter uma variável de precisão dupla. Finalmente, após um atraso adicional, os resultados são retornados sob o Executar o Loop de um botão.

      Como apenas observadas, quatro conjuntos separados de cálculos foram realizados simultaneamente em quatro segmentos separados. A interface do usuário permaneceu ágil de entrada e os resultados foram retornados após cada thread concluída.

Coordenação de seus segmentos.

Um usuário experiente de aplicativos multithread pode percebê-uma sutil falha com o código como 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 varTotalCalculations e defina a variável de local varTotalAsOfNow a este valor. Esse valor é retornado ao frmCalculations e exibidos em um controle de rótulo. Mas está sendo retornado o valor correto? Se apenas um único segmento de execução está sendo executado, a resposta é Sim. Mas, se estiver executando vários segmentos, a resposta se torna mais indefinida. Cada segmento tem a capacidade de incrementar a variável varTotalCalculations. É possível que essa variável é incrementado após um segmento, mas antes ele copia o valor de varTotalAsOfNow, outro thread podia alterar o valor dessa variável aumentando o proprietário. Isso leva à possibilidade de que cada thread é, na verdade, relatório de resultados imprecisos. Visual C#fornece a <>bloqueio de>instrução (referência de TRANSLATION FROM VPE FOR CSHARP) para permitir a sincronização de threads para garantir que cada thread sempre retorna resultados precisos. A sintaxe para lock é o seguinte:

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, a execução da expressão especificada é bloqueada até que o segmento especificado tem um bloqueio exclusivo do objeto em questão. No exemplo mostrado acima, a execução está bloqueada no AnObject. lockdeve ser usado com um objeto que retorna uma referência em vez de um valor. A execução, em seguida, poderá passar como um bloco sem interferência de outros segmentos. Um conjunto de instruções que são executados como uma unidade é considerada atômica. Quando o } é encontrado, a expressão é liberada e os threads têm permissão para continuar normalmente.

Para adicionar a instrução lock para seu aplicativo.

  1. Abrir Calculator.cs no O Editor de código.

  2. Localize 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.

  3. Modificar este código para que ela seja lida da seguinte maneira:

    lock(this)
    {
       varTotalCalculations += 1;
       varTotalAsOfNow = varTotalCalculations;
    }
    
  4. Salve seu trabalho e testá-lo como no exemplo anterior.

    Você pode perceber um pequeno impacto no desempenho do seu programa. Isso ocorre porque a execução de threads pára quando um bloqueio exclusivo é obtido em seu componente. Embora garante a exatidão, essa abordagem impede que alguns dos benefícios de desempenho de vários segmentos. Cuidadosamente, você deve considerar a necessidade de threads de bloqueio e implementá-los 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 Visual Basic

Referência

BackgroundWorker

Conceitos

Event-based Asynchronous Pattern Overview

Outros recursos

Programando com Componentes

Explicações Passso a Passo sobre Programação de Componentes

Multithreading em componentes