Trabalhando com Threads em formulários Windows
As operações com acesso a banco de dados geralmente consomem recursos que podem impactar a sua aplicação. Se você tem que exibir um volume muito grande de dados em um formulário Windows usando um DataGridView tem que considerar que a aplicação pode ficar em estado de espera por um certo período de tempo e, isto pode ser um fator indesejável se este período for muito longo.
Uma das maneiras de evitar este efeito indesejável e diminuir o tempo de espera do usuário é usar Threads.
Usando threads você pode executar a consulta contra o banco de dados e preencher o DataGridView em uma linha de execução distinta liberando assim a sua aplicação para realizar outras tarefas ao invés de ficar esperando que a execução da consulta termine e os dados sejam retornados e exibidos no formulário.
Ocorre que os formulários Windows são baseados em um modelo STA - single-threaded apartment; eles podem ser criados em qualquer thread, mas depois que eles foram criados não podem mudar para uma thread diferente, e os métodos dos formulários Windows também não podem ser acessados em outra thread distinta daquela na qual eles foram criados. Isto significa que todos os métodos dos formulários (e os controles criados na mesma thread) devem ser executados na thread que criou o formulário ou controle do formulário.
Se você tentar chamar um método de um formulário windows em uma thread distinta, uma exceção será disparada, e, dependendo de como você implementou o tratamento de exceção no seu código a aplicação pode encerrar. Se você não estiver efetuando um tratamento de exceção será exibida a seguinte mensagem:
An unhandled exception of type 'System.ArgumentException' occurred in system.windows.forms.dll
Então para seu próprio bem tenha em mente sempre o seguinte:
"Quando você estiver trabalhando com controles em formulários windows tenha sempre certeza de somente acessar os controles a partir da thread na qual eles foram criados. senão..."
Como usar multithreading em aplicações com formulários windows ?
Veja bem, quando você for chamar um método de um controle que esta em um formulário windows fora da thread de origem do controle você tem que executar a chamada na thread de origem.(a chamada tem que ser 'marshalled') . Existem duas formas de você fazer isto:
De forma assíncrona - Usar o método BeginInvoke que força o método a ser executado na thread que criou o formulário ou controle. Se você usar BeginInvoke a chamada irá retornar imediatamente. Se você precisar obter o valor de retorno de um delegate invocado de forma assíncrona você pode usar o EndInvoke com o IAsyncResult retornado pelo BeginInvoke para esperar até que o delegate invocado assincronamente tenha sido completado.
De forma síncrona - Usar o método Invoke para obter o mesmo efeito. Quando você usar Invoke a thread atual será bloqueada até que o delegate tenha sido executado.
Todos os controles de formulários windows possuem o método Invoke e a propriedade InvokeRequired.
Você usa o método Invoke para fazer com que o controle transfira a chamada para a thread na qual ele foi criado e então executar a tarefa proposta.
A propriedade InvokeRequired retorna um valor true/false (false indica que a thread atual é a thread da fila de mensagem) indicando se quem chamou o método precisa usar o método Invoke quando a chamada é feita a um método do controle. Exemplo de uso:
- No exemplo abaixo eu estou definindo um delegate e estou verificando a propriedade InvokeRequired da caixa de texto para ver se a chamada ao delegate tem origem na thread de origem do controle ou vem de outra thread.
Public Delegate Sub delegando(ByVal texto
As String)
Public Sub chamaMetodo(ByVal texto As
String) |
Para encerrar utilize sempre estas duas regras de ouro (golden rules)
1- Nunca invoque qualquer método ou propriedade em um controle criado em outra thread sem usar os métodos/propriedades: Invoke, BeginInvoke, EndInvoke or CreateGraphics, e InvokeRequired.
Cada controle esta vinculado a uma thread que executa a sua fila de mensagens. Se você tentar acessar ou alterar qualquer coisa na interface,(por exemplo alterar a propriedade Text) a partir de uma thread diferente você corre o risco de travar o seu programa.
2- Nunca execute uma tarefa que vai consumir muito tempo em uma thread de um controle/formulário windows forms.
Se o seu código esta sendo executado em uma thread de controle/formulário nenhum outro código está rodando nesta thread. O que significa que você não irá receber notificações de eventos, seus controles não serão redesenhados , etc. E você terá que esperar que o código termine de ser executado para realizar outras tarefas. Geralmente os programadores VB poderiam ficar tentados a usar uma chamada a Application.DoEvents. Mas isto não é aconselhável.
Até o próximo artigo...
José Carlos Macoratti