 VB .NET - 
Programação 
Assíncrona com Thread Pools
VB .NET - 
Programação 
Assíncrona com Thread Pools
Quando você esta desenvolvendo uma aplicação pode se deparar com uma situação onde existe a necessidade de realizar um processamento mais intenso que pode fazer com que sua aplicação sofra uma perda de desempenho dando a sensação ao usuário que a aplicação esta 'congelada' .
Se você não precisa aguardar o término deste processamento para que o usuário interaja com a aplicação pode usar o recurso do ThreadPool para obter um comportamento assíncrono sem ter que recorrer ao processamento de MultiThreads.
| MultiThreads ??? Meu Deus !!! O que é isto ???? Calma !! O 
    termo MultiThread define a capacidade de se 
    executar múltiplos 
    processos ao mesmo tempo de forma independente. 
    Ou seja você executa duas tarefas (assobiar e chupar cana) ao mesmo 
    tempo e uma tarefa não depende da outra para ser executada. Vamos dar um exemplo para clarear : Suponha que você queira carregar um controle ListView com os dados de uma tabela e que ao mesmo tempo queira também preencher o conteúdo de uma Combobox com os dados de outra tabela , bem se conseguir realizar tal proeza ao mesmo tempo e de forma independente você esta realizando um processamento MultiThread. Ora , ora você pode estar pensando com os seus botões que o VB6.0 suporta o processamento MultiThread ... Mas eu lhe digo que esta enganado , na verdade quando você tenta fazer tal tarefa usando o VB 6.0 a tarefa é feita sequencialmente ; só que a coisa é tão rápido (se a tabela tiver poucos registros) que parece , eu disse parece, que as tarefas são realizadas simultâneamente.O que o VB6.0 faz é executar múltiplos apartamentos dentro de um único processo. No VB.NET a multiTarefa (MultiThread) é uma realidade , ou seja , podemos ter múltiplos processos paralelos que podem acessar o mesmo conjunto de dados compartilhados. | 
A plataforma .NET mantém um pool de threads prontas para serem usadas que são idéias para realizar tarefas rápidas. Geralmente essas threads são usadas para operações assíncronas de acesso a arquivos ou operações realizadas pela chamada de um Delegate ou BeginInvoke.
| Delegates são tipos usados para invocar um ou mais métodos onde o método atual invocado é determinado em tempo de execução. Delegates provê uma forma de invocar método pelo seu endereço ao invés de seu nome. | 
Como estas tarefas são rápidas a criação e destruição das threads passa ter um porção significativa no tempo de execução da tarefa. Para evitar que gerenciamento de linhas de execução (Threads) afete o desempenho, a .NET Framework cria um pool de threads quando necessário até um valor limite e então mantém as threads do pool em estado de espera e prontas para a próxima operação assíncrona. Com isso é ocupada apenas uma pequena quantidade de memória para cada thread otimizando o desempenho.
A classe System.Threading.ThreadPool fornece um número de métodos estáticos que permitem que você monitore e controle as threads do pool.
Vejamos os mais importantes:
- GetMaxThreads - Retorna o número
máximo das threads ativas no pool.
- GetAvailableThreads- Retorna a
diferença entre o número máximo de threads do pool e o número
atual de threads ativas.
- GetMinThreads - Retorna o número
de threads ociosas que o pool mantém e espera de novas
requisições.
- SetMinThreads - Altera o valor
mínimo de threads disponíveis no pool. Se você diminuir
muito o número de threads ociosas pode afetar o desempenho do
sistema;
- SetMaxThreads - Altera o valor
máximo de threads disponíveis no pool;
- QueueUserWorkItem - Enfileira um
método para execução. O método será executado quando uma
thread do pool estiver disponível. Geralmente você usa este
método para executar um processo em outra thread. Pode ser usado
da seguinte forma:
| QueueUserWorkItem(WaitCallBack) | Enfileira um método para execução onde o método executa quando a thread estiver ativa. | 
| QueueUserWorkItem(WaitCallBack, Object) | Enfileira um método para execução e especifica um objeto contendo os dados para ser usado pelo método. O método executa quando a thread estiver disponível. | 
Para exemplificar a utilização destes métodos vamos usar o Visual Basic 2008 Express Edition para criar uma aplicação do tipo Console com o nome de threadPool_1 digitando o código abaixo:
| Imports System.Threading
Module Module1
    Sub Main()
        ' exibe o estado padrão das threads do pool
        exibeEstadoThreads() '
        ' Altera os parâmetros do Pool de threads
        Console.WriteLine("Alterando o numero de Threads...")
        If Not ThreadPool.SetMaxThreads(100, 500) Then
            Console.WriteLine("Chamada a SetMaxThreads falhou....")
        End If
        If Not ThreadPool.SetMinThreads(25, 25) Then
            Console.WriteLine("Chamada a SetMinThreads falhou....")
        End If
        ' Inicia uma thread do pool
        ThreadPool.QueueUserWorkItem(AddressOf NaofazNada)
        Thread.Sleep(10)
        ' Mostra o novo estado da thread
        exibeEstadoThreads()
        ' Aguarda para encerrar o programa
        Console.WriteLine()
        Console.WriteLine("Pressione Enter para encerrar...")
        Console.ReadLine()
    End Sub
    Sub exibeEstadoThreads()
        ' Retorna o número máximo de threads do pool
        Dim threadsAtivas As Integer
        Dim threadsTerminadas As Integer
        ThreadPool.GetMaxThreads(threadsAtivas, threadsTerminadas)
        Console.WriteLine("Máximo de threads ativas={0}" & vbCrLf & "Máximo de I/O threads={1}",  threadsAtivas, threadsTerminadas)
        ' Retorna o no mínimo de threads ociosas
        ThreadPool.GetMinThreads(threadsAtivas, threadsTerminadas)
        Console.WriteLine("Minino de threads ativas={0}" & vbCrLf & "Minimo de I/O threads={1}", threadsAtivas, threadsTerminadas)
        ' Mostra threads disponíveis
        ThreadPool.GetAvailableThreads(threadsAtivas, threadsTerminadas)
        Console.WriteLine("Threads ativas disponíveis={0}" & vbCrLf & "Threads I/O disponíveis={1}",  threadsAtivas, threadsTerminadas)
    End Sub
    Sub NaofazNada(ByVal state As Object)
        Thread.Sleep(1000)
        Console.WriteLine("Sem fazer nada...")
    End Sub
End Module | 
Executando este projeto iremos obter o seguinte resultado:
|  | 
Usando ThreadPool em uma aplicação Windows Forms
Como já foi mencionado a classe ThreadPool do namespace System.Threading contém um pool virtual de threads disponíveis que você pode usar. Você pode usar a classe ThreadPool para realizar operações assíncronas com recurso de Multithread sem ter que escrever código para gerenciar o pool de threads.
A classe ThreadPool gerencia um grupo dinâmico de threads que estão disponíveis para realizar o processamento multithread. O número de threads no pool é dinâmico e você não é obrigado realizar nenhum trabalho extra para gerenciar as threads no pool. Se você requisitar uma thread e não existir nenhuma disponível, o pool pode criar uma nova thread ou esperar até que uma thread esteja disponível.
O gerenciamento das threads é feita pelo pool de forma transparente e você não tem que se preocupar com esse detalhe apenas com o seu código.
Para demonstrar como usar o recurso ThreadPool e obter o mesmo resultado obtido pela construção de threads vamos propor um problema que simula uma aplicação Windows com um grande processamento inicial. Para forçar uma simulação a aplicação Windows irá carregar 100 milhões de números em um controle ListBox.
Se a aplicação simplesmente tentar carregar os números inteiros no controle ListBox no evento Load do formulário o formulário irá demorar vários minutos para ser exibido mas, se executarmos esta operação em uma thread separada, então o nosso formulário irá exibir o controle ListBox de imediato.
Nota: Observe que podemos usar este recurso pois não precisamos que o processamento de carregar os números na LIstBox esteja concluído para poder exibir a ListBox e aguardar a interação do usuário.
Usando o recurso ThreadPool o formulário será carregado de imediato enquanto a LIstBox continua a ser carregada em segundo plano ficando disponível para a interação com o usuário.
Abra o Visual Basix 2008 Express Edition e crie um novo projeto do tipo Windows Forms Application com o nome threadPool_2 ;
Inclua no formulário padrão form1.vb um controle ListBox e um botão de comando Button conforme o leiaute abaixo:
|  | 
Defina o namespace
Imports
System.Threadingpara termos acesso a classe ThreadPool.
No evento Load do formulário vamos incluir o código:
| Private
    Sub Form1_Load(ByVal 
    sender As 
    System.Object, ByVal 
    e As 
    System.EventArgs) Handles
    MyBase.Load 
    ListBox1.Items.Clear() | 
Estamos usando o método QueueUserWorkItem da classe ThreadPool para enfileirar a execução da rotina carregaLista quando uma thread do pool estiver disponível.( Este método é estático (Shared) e por isso não precisamos criar uma instância da classe ThreadPool)
Vejamos agora o código da rotina carregaLista():
| PrivateSub carregaLista(ByVal state As Object) Dim i As Double SyncLock ListBox1.GetType For i = 10000000000000000 To 1 Step -1 item = i ListBox1.Invoke(CType(AddressOf incluir, MethodInvoker)) NextEnd SyncLock End Sub | 
Esta rotina irá efetivamente carregar o controle ListBox com milhões de números. Observe que o código esta sendo executado no interior de um SyncLock.
A instrução SyncLock garante que múltiplas threads não executem as instruções do bloco ao mesmo tempo e previne que cada thread inicie a execução do código no bloco até que a outra thread terminou de executar o mesmo o código.
Resta agora exibir o código da rotina incluir :
| Private
    Sub 
    incluir() ListBox1.Items.Add(item) | 
Neste código estamos incluindo os valores no controle ListBox.
O método Application.DoEvents permite que seu aplicativo manipule outros eventos que podem ser disparados enquanto seu código é executado. O método My.Application.DoEvents possui o mesmo comportamento que o método DoEvents.
Executando o projeto teremos a exibição do formulário com o controle ListBox já preenchido e a rotina para preenchimento continua sendo executada em outra thread.
Observe que usamos a thread a partir do pool através da classe ThreadPool de forma mais simples e sem preocupação em gerenciar threads.
Uma palavra final sobre Threads em aplicações ASP .NET. Com certeza podemos também usar o recurso das threads nas aplicações ASP .NET para por exemplo executar processos que envolvem grandes processamentos no servidor. Como o assunto merece um tratamento a parte irei publicar um artigo a respeito em breve.
Eu sei é apenas VB .NET, mas eu
gosto...
referências:
José Carlos Macoratti