Quando você está desenvolvendo uma aplicação, pode se deparar com uma situação onde existe a necessidade de realizar um processamento mais intenso. Isso pode fazer com que sua aplicação sofra uma perda de desempenho dando a sensação ao usuário que a aplicação está '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 está 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ápida (se a tabela tiver poucos registros) que parece - eu disse parece - que as tarefas são realizadas simultaneamente. 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 a ter uma 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:
| 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 ModuleExecutando este projeto, iremos obter o seguinte resultado:

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, ele 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.Threading para 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()
ThreadPool.QueueUserWorkItem(AddressOf carregaLista)
End SubEstamos 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():
Private Sub 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))
Next
End SyncLock
End Sub Esta rotina irá efetivamente carregar o controle ListBox com milhões de números. Observe que o código está 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)
Application.DoEvents()
End SubNeste 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 à parte irei publicar um artigo a respeito em breve.
Eu sei, é apenas VB .NET, mas eu gosto...
2001 - iMasters FFPA Informática Ltda - Todos os direitos reservados.