Translate this page now :



»Programação
»Programação.NET
»Banco de Dados
»Webdesign
»Office
» Certificações Microsoft 4
»Treinamentos4
»Programação 4
»Webdesign«
»Office & User Tips«
»Grupos de Usuários
»Células Acadêmicas«
intcontpiada : 118
Evoluindo
Você já está cadastrado e participa do grupo de usuários de sua cidade ? Se não, comente o porque.
 
 
Faça um pequeno teste com 10 questões de VB
.:.
Teste seus conhecimentos em Visual Basic, SQL Server e ASP 3.0 com nossas provas on-line
.:.
Aprimore seus conhecimentos em programação com nosso treinamento on-line de lógica de programação
.:.
Veja nosso calendário de treinamentos
Gostou da Página?
Então

para um amigo!
 







Pesquisa personalizada
Pesquisar Dicas:

 







MultiThreading e CallBacks

Com certeza você, assim como eu e muitos outros, está acostumado a trabalhar com múltiplas aplicações simultaneamente em seu micro. Deixar um download acontecendo em background enquato escreve um texto ou deixar o messenger conectado enquanto escreve um e-mail já viraram tarefas corriqueiras.

Mas como o sistema operacional organiza isso tudo ?

Desde o surgimento do Windows 95 o sistema operacional utiliza um recurso que é chamado de multitarefa preemptiva. Com a multitarefa preemptiva é o sistema operacional que divide o tempo de uso de processador entre as aplicações que estão rodando em nossa máquina, nos dando a impressão de que tudo roda simultaneamente, o que não é verdade, pois em geral temos apenas um processador.

Cada aplicação, ao ser executada, ganha um espaço de memória isolado para sua execução, conhecido como processo. Os processos são totalmente isolados uns dos outros, uma aplicação em um processo não pode ver diretamente outras aplicações em outros processos.

Mas não é o processo que ganha tempo de execussão do processador. Quem ganha tempo de execussão do processador são as Threads, unidades de execussão de código. Cada aplicação tem pelo menos uma thread e o sistema operacional divide o tempo do processador entre as diversas threads em execussão na máquina.

No VB 6 não podiamos criar múltiplas threads, ficávamos sempre com a única thread gerada pelo processo. Mas no .NET temos agora muita facilidade em criar múltiplas threads. A criação de múltiplas threads pela mesma aplicação faz com que a aplicação ganhe mais tempo de processamento do que as demais, pois haverão mais threads na mesma aplicação ganhando tempo de processamento.

Outra vantagem do uso de múltiplas threads é o trabalho assincrono. Com uma única thread, se a nossa aplicação possui alguma tarefa longa para ser realizada o usuário precisa ficar aguardando a tarefa encerrar para só então continuar o trabalho, ou seja, a tarefa é realizada de forma síncrona.

Com o uso de multithreading podemos disparar a tarefa em uma thread a parte enquanto o usuário continua fazendo seu trabalho na thread principal da aplicação. Quando a tarefa na thread a parte se encerra o usuário é avisado que a tarefa que ele solicitou já se encerrou.

Essa execução de uma tarefa em uma thread a parte é chamada de execussão assíncrona, pois o usuário não precisa ficar esperando a tarefa terminar. A tarefa então precisa avisar a aplicação e, consequentemente, ao usuário, que terminou. Isso normalmente é feito através de um CallBack : Passa-se como parâmetro para a thread um ponteiro para uma função de forma que a thread poderá chamar quando seu trabalho terminar, avisando assim do térmido da tarefa.

Anteriormente isso poderia ser algo muito dificil de criar, mas agora contamos com a facilidade dos Delegates que funcionam como ponteiros para funções, nos permitindo exatamente fazer uso do recurso de CallBack.

Então vamos criar uma pequena aplicação, um pequeno exemplo de uma aplicação multithreading com chamadas assíncronas e CallBacks. Vamos começar montando a parte gráfica :

- Crie uma nova Windows Application
- Crie mais um Windows form (form2)
- Insira um botão no Form1 e de o nome de cmdNovaJanela
- No form2 crie um label (estará com o nome de label1)


O Form1 será o responsável por disparar o Form2 e fazer uma contagem sequencial usando o label do form2. Faremos com que várias cópias do form2 possam ser disparadas e possam ficar rodando simultaneamente.

Quando o form2 for fechado deverá disparar um callBack para para o Form1 avisando ao Form1 que a tarefa foi completada.

Vamos então criar no Form1 um delegate para que o form2 possa dispara-lo, veja :

Public Delegate Sub Back(ByVal f As Form2)
Public b As Back

O Delegate guarda certa semelhança com a forma de uso de uma classe. O b foi definido como sendo do tipo da classe e precisaremos criar uma instância do b, vamos fazer isso no evento Load do form1.

 Private Sub Form1_Load(ByVal sender As Object, ByVal e As System.EventArgs) Handles MyBase.Load
     b = New Back(AddressOf Retorno)
 End Sub


A declaração do Delegate definiu uma assinatura : Uma sub recebendo um parâmetro do tipo Form2. Ao fazermos o new do delegate passamos o endereço de uma sub que tenha a mesma assinatura. Em nosso exemplo utilizaremos uma sub chamada "retorno", criada dentro do form1.

O form2 então precisará de uma variável do tipo "back" para poder receber por parâmetro o delegate que deverá ser chamado. Iremos também criar uma variável boleana que servirá para indicar o término do processamento de contagem que iremos realizar. Teremos então no form2 as seguintes declarações :

Public rodando As Boolean
Public b As Form1.Back


Vamos então fazer uma sub para gerar uma nova instancia do form2 e iniciar o processamento com esta nova instância. Veja :

 Sub abrirNovoForm()
     Dim f As New Form2()
     Dim iCNT As Integer
     f.Show()
     f.b = b
     f.rodando=true
     Do While f.rodando
         f.Label1.Text = iCNT
         Application.DoEvents()
         iCNT += 1
     Loop
     f.Dispose()
     f = Nothing
 End Sub

Veja o que esta sub faz :

A idéia então é que está sub seja chamada diversas vezes, mas será executada em diferentes threads (quem a chamar terá que cuidar disso) para desta forma os formulários poderem ter sua contagem rodando simultaneamente.

A essa altura você deve estar perguntando : Se estamos usando multitarefa preemptiva, onde o sistema operacional dividirá o tempo de execução entre as aplicações, então por que chamar o DoEvents em nosso código ?

Ocorre que tanto a sub que descrevemos acima como uma instância do form2 estarão rodando na mesma thread. A sub gera um loop continuo e atualiza o conteúdo de um label. Porém, devido ao loop continuo, o label não teria tempo de se redesenhar e não veríamos nada sendo exibido. O que o DoEvents faz é verificar se existem outras tarefas pendentes NA MESMA THREAD, no caso, o redesenho do label.

Agora precisamos criar um disparo para esta função. Vamos fazer isso no clique do nosso botão, no Form1, veja como fica :

 Private Sub cmdNovaJanela_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles cmdNovaJanela.Click
     Dim th As New Threading.Thread(AddressOf abrirNovoForm)
     th.Start()
  End Sub


Apenas essas duas linhas são o bastante. Com a 1a linha criamos uma nova thread e dizemos que desejamos que esta thread rode a sub "abrirnovoform". Já na 2a linha fazemos o start da thread, consequentemente da sub, que vai exibir o form2.

Agora temos que cuidar do encerramento da tarefa. Quando o form2 for fechado precisaremos disparar o CallBack. Lembra-se que ao abrir o Form2 guardamos o CallBack na variável "b" ?

 Private Sub Form2_Closed(ByVal sender As Object, ByVal e As System.EventArgs) Handles MyBase.Closed
     b.Invoke(Me)
 End Sub


Assim sendo no evento closed, que ocorre quando o form2 foi fechado, fazemos o disparo do CallBack transmitindo a própria instância do form2 como parâmetro. Você se lembra que o Delegate recebe, de acordo com sua assinatura, uma instância do form2, não é ?

Por fim vamos desenvolver a sub "retorno", que receberá a chamada do CallBack. Eis a sub :

 Public Sub Retorno(ByVal f As Form2)
     MsgBox("O formulário parou em :" & f.Label1.Text)
     f.rodando = False
 End Sub

Observe que não é necessário pararmos a thread. Ao atribuirmos false ao f paramos o laço que está sendo executado dentro da thread e a thread será parada naturalmente.

Neste nosso exemplo a sub que abre o form2 faz um loop contínuo, para demonstrar a atividade em paralelo. E se não tivessemos esse loop ? E se apenas desejassemos abrir o form em outra thread, mas sem esse processamento imediato ?

Veja como ficaria uma nova versão desta sub :


     Public Sub abre2()
           Dim f As New Form2()
            f.b = b
           Application.Run(f)
     End Sub


Observe a utilização do Application.Run . Toda Thread precisa ter uma execução contínua, o processador nunca pode estar desocupado. Se a execução da thread parar a thread é imediatamente eliminada. Então o application.Run ativa essa execução contínua da Thread, sendo encarregado inclusive de exibir o formulário f.

No exemplo acima, se não utilizassemos o application.Run, mal veriamos o formulário, ele seria criado e eliminado imediatamente, devido a parada de execução da thread. Mas com o application.Run vemos o formulário na tela e podemos iniciar atividades a partir dele, tudo rodando em uma thread isolada.

Esse é apenas o inicio do trabalho mutithreading. O uso de threads é algo bem complexo e, dependendo do objetivo, pode exigir um estudo mais profundo do funcionamento do SO.

Dennes Torres
MCAD,MCSD,MCSE,MCDB
A

� Búfalo Informática, Treinamento e Consultoria - Rua Álvaro Alvim, 37 Sala 920 - Cinelândia - Rio de Janeiro / RJ
Tel.: (21)2262-1368 (21) 9240-5134 (21) 9240-7281 e-Mail:
contato@bufaloinfo.com.br