• Revista PROGRAMAR: Já está disponível a edição #53 da revista programar. Faz já o download aqui!

C0PILHA

[Resolvido] Thread "gulosa" - Thread ocupa todo processamento do Projecto

9 mensagens neste tópico

Primeiramente, olá a todos e obrigado, pois já fico agradecido pela atenção que tenho certeza que me prestarão desde agora.

Bom... Meu problema começou quando decidi me actualizar de VS 2003 para o VS 2008, quase que tudo que está relacionado a código e que é muito utilizado permaneceu o mesmo, mas algumas coisas mudaram, sei que já sabem, por exemplo as threads:

Utilizarei um exemplo em VB, que concidero o "Algoritmo das Linguagens de programação", se conseguir fazer nele, consigo nas outras também do VS 2008.

No 2003 um código simples de uma thread era practicamente assim...

.

.

  public Sub Exemplo()

      Do Until TextBox1.Top > 100

   

        TextBox1.Top = TextBox1.Top + 1

        Thread.Sleep(10)

   

      Loop

  End Sub

.

.

.

  Dim var As new System.Threading.Tread(AdressOf Exemplo)

  var.Start()

.

.

.

Assim era no VB 2003, mas agora no VB 2008 apareceu uma firula de colocar delegates no código para poder acessar componentes do formulário, uma obra bem faraônica pro meu pensar.

 

  Para me adaptar ao VS 2008 resolvi fazer um projetinho bem simples, um joguinho de Bisca, uma variação da Sueca, só que para fazer as cartas andarem precisaria de uma thread para dar o efeito. :P

  Resolvi fazer um teste com um formulário com dois TextBox para depois adaptá-lo ao jogo embasado no código mais fácil e mais bem explicado que eu achei em incansáveis buscas na internet, chegou a "gastar o google" rs, do nosso amigo moderador do site, o Sr. Paulino, que, atenciosamente, me encaminhou ao site sujerindo qu'eu postasse o código.

Aqui está pessoal: ...

Imports System.Threading

Public Class ThreadTest

    ' Um delegate é uma referência ou apontador a um

    ' método e como a thread em si não pode executar

    ' operações é necessário utiliza-lo

    Delegate Sub myDelegate()

    ' Chama o delegate

    Private Sub DeleDaMexeCaxa()

        If Me.InvokeRequired Then

            Me.Invoke(New myDelegate(AddressOf MexeUmaCaixa))

        End If

    End Sub

    Private Sub DeleDaMexeCaxa2()

        If Me.InvokeRequired Then

            Me.Invoke(New myDelegate(AddressOf MexeAOutraCaixa))

        End If

    End Sub

    Private Sub MexeUmaCaixa() ' Essa Sub é responsavel por fazer a TextBox1 descer

        Do Until TextBox1.Top > 100

            TextBox1.Top = TextBox1.Top + 1

            Thread.Sleep(10)

        Loop

    End Sub

    Private Sub MexeAOutraCaixa() ' Essa Sub é responsavel por fazer a TextBox2 subir

        Do Until TextBox2.Top < 100

            TextBox2.Top = TextBox2.Top - 1

            Thread.Sleep(10)

        Loop

    End Sub

    Private Sub Button1_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles Button1.Click

        ' Cria e inicia a Thread responsável por mover a TextBox1 para baixo no Form

        Dim DesceTxt As New Threading.Thread(AddressOf DeleDaMexeCaxa)

        '

        DesceTxt.Priority = ThreadPriority.Normal

        '

        DesceTxt.IsBackground = True

        '

        DesceTxt.Start()

        ' Cria e inicia a Thread responsável por mover a TextBox2 para cima no Form

        Dim SobeTxt As New Threading.Thread(AddressOf DeleDaMexeCaxa2)

        '

        SobeTxt.Priority = ThreadPriority.Normal

        '

        SobeTxt.IsBackground = True

        '

        SobeTxt.Start()

    End Sub

End Class

Nesse projecto eu desenhei um Form com uma TXT em cima e outra em baixo, e no meio delas um botão que chamava a Thread, quando uma TXT está subindo, o form fica inascessível, não tem como minimizar/fexar, parece que trava com a TXT subindo, e, era para as duas inicializarem juntas, uma subir e a outra descer paralelamente e em união, e isso não acontece, enquanto uma não terminar o trabalho a outra não começa, era como se não existisse a Thread.

Bom pessoal, estou esperando a ajuda de vocês, agradeço desde já, e perdoem-me pelo incômodo.

0

Partilhar esta mensagem


Link para a mensagem
Partilhar noutros sites

Isso acontece porque, ao fazeres Thread.Sleep(10), estás de facto a dizer à thread responsável pela actualização da GUI para adormecer, e como estás a executar o movimento de ambas as caixas na mesma thread (da GUI, como deve ser :P ), o movimento não irá ser executado em paralelo. Só quando terminar de mover uma caixa é que irá mover a outra.

Tens várias hipóteses para corrigir isso, umas mais correctas que as outras.

Começando pela mais incorrecta (e mais fácil de implementar), podes passar o código que move as caixas para uma única Sub, e mexes ambas dentro do mesmo Do...Until, passando igualmente a existir um único Delegate. Isto é incorrecto porque a GUI continuará inacessível até que o movimento acabe. Uma das regras para o código a executar na thread da GUI é NUNCA executar código demorado nessa thread.

Um pouco mais correcto seria fazer os Do...Until dentro dos Delegates (dentro do If(Me.InvokeRequired)), retirando-os das Subs Invoke'adas.

A melhor solução das que apresento, seria a junção das duas sugestões: passar os Do...Until para o delegate e ter uma só Sub e um só Delegate para mover ambas as caixas.

0

Partilhar esta mensagem


Link para a mensagem
Partilhar noutros sites

  Obrigado pela atenção TheDark, fico agradecido.  :)

 

  Mas não tem como fazer parecido como eu fazia antes no VS 2003 ? Com duas Threads ?

  Porque lá eu usava e abusava de por "Thread.Sleep(x)" nas threads e dava certo.  :hmm:

  E eu já tentei fazer com uma sub só e um delegate, mas mesmo assim ele trava o programa.

  Por que neste caso até dá certo, mas tem algumas outras ocasiões, principalmente na criação de jogos, que eu só sei fazer do jeito antigo. =/

  E... Não querendo abusar, mas já abusando  :) , eu gostaria de saber o porquê deles terem feito desse jeito no VS 2008, mais "chato" pra fazer esse tipo de coisa. 

  Mais uma vez muito obrigado. :)

0

Partilhar esta mensagem


Link para a mensagem
Partilhar noutros sites

  Mas não tem como fazer parecido como eu fazia antes no VS 2003 ? Com duas Threads ?

  Porque lá eu usava e abusava de por "Thread.Sleep(x)" nas threads e dava certo.  :hmm:

Podes usar duas threads à mesma, foi a minha segunda sugestão:

Um pouco mais correcto seria fazer os Do...Until dentro dos Delegates (dentro do If(Me.InvokeRequired)), retirando-os das Subs Invoke'adas.

mas corres o risco (embora improvável) de uma subir mais depressa que a outra.

  E eu já tentei fazer com uma sub só e um delegate, mas mesmo assim ele trava o programa.

Podes mostrar como fizeste?

  Por que neste caso até dá certo, mas tem algumas outras ocasiões, principalmente na criação de jogos, que eu só sei fazer do jeito antigo. =/

Mas este problema só se põe quando queres alterar algo na GUI, como a posição das caixas. Para outros problemas estão disponíveis outros mecanismos para partilha de dados: locks intrínsecos, Monitores, e toda uma cadeira de Programação Concorrente que estou a terminar :D

  E... Não querendo abusar, mas já abusando  :) , eu gostaria de saber o porquê deles terem feito desse jeito no VS 2008, mais "chato" pra fazer esse tipo de coisa. 

Abuso nenhum. Perguntar não ofende :)

Segundo percebi (e posso ter percebido mal, uma vez que nunca estudei .NET 1.1), o que me parece é que a versão 2003 não impunha confinamento aos elementos da GUI, e assim qualquer thread podia alterar os dados da GUI.

Com o .NET 2.0, e o VS 2005 e posteriores, o multithreading ficou mais robusto e deixou de permitir que qualquer thread mexa nos valores da GUI. O que dantes era mais simples de fazer, tornou-se necessariamente mais complicado para melhorar o multithreading. É um mal necessário para evitar corrupção de dados.

Não vou estar com explicações mais detalhadas porque é um assunto bastante complexo e tenho medo de me enganar :P

Mas basicamente o que há a fazer agora é que, quando uma thread (que não seja a responsável pela GUI) quer alterar algo relativo à GUI, seja o estado de um botão, a posição de um controlo, ou o texto de uma TextBox, tem que fazer um Me.Invoke para pedir à thread daGUI que altere o estado desse controlo.

  Mais uma vez muito obrigado. :)

De nada! Enquanto houver folga, está tudo bem :)

0

Partilhar esta mensagem


Link para a mensagem
Partilhar noutros sites

  Ah sim, perdoe-me, não tinha compreendido a questão dos dois do Until, agora intendi, mas eu já tinha modificado o código, vou tentar refazê-lo pra postar...

  Mas eu consigui achar algo que também serve, é o código que eu disse que peguei em um post do Sr. Jorge Paulino sobre multithreads e que fiz algumas modificações.

  Esse é o código original ....

Imports System.Threading

Public Class ThreadTest

' Um delegate é uma referência ou apontador a um

' método e como a thread em si não pode executar

' operações é necessário utiliza-lo

Delegate Sub myDelegate()

' Chama o delegate

Private Sub backupThreadMain()

    If Me.InvokeRequired Then

        Me.Invoke(New myDelegate(AddressOf startBackUp))

    End If

End Sub

' Inicia o backup

Private Sub startBackUp ()

    ' código para cópia/backup

End Sub

' Código inicial do form

Private Sub frmBackUp_Load(ByVal sender As Object, ByVal e As System.EventArgs) Handles MyBase.Load

    ' Cria e inicia uma nova thread

    Dim bkThread As New Threading.Thread(AddressOf backupThreadMain)

    bkThread.Priority = ThreadPriority.Highest

    bkThread.IsBackground = True

    bkThread.Start()

End Sub

End Class

  Com esse código eu fiz uma pequena modificação...

Imports System.Threading

Public Class ThreadTest

' Um delegate é uma referência ou apontador a um

' método e como a thread em si não pode executar

' operações é necessário utiliza-lo

Delegate Sub myDelegate()

' Chama o delegate

Private Sub backupThreadMain()

    If Me.InvokeRequired Then

        Me.Invoke(New myDelegate(AddressOf startBackUp))

    End If

End Sub

' Inicia o backup

Private Sub startBackUp ()

 

  do until (TextBox1.Top > 100)

 

      TextBox1.Top = TextBox1.Top + 1

      Thread.Sleep(10)

  Loop

End Sub

' Código inicial do form

Private Sub frmBackUp_Load(ByVal sender As Object, ByVal e As System.EventArgs) Handles MyBase.Load

    ' Cria e inicia uma nova thread

    Dim bkThread As New Threading.Thread(AddressOf backupThreadMain)

    bkThread.Priority = ThreadPriority.Highest  ' Eu tentei por normal aqui mas naum adianta

    bkThread.IsBackground = True

    bkThread.Start()

End Sub

End Class

Esse código aqui também trava o programa, deixa só a caixinha de texto a subir e o form travado.  :cheesygrin:

Eu pensava que o thread.sleep(x) deixava só a thread dormindo; o prog em sí, continuava rodando.

Teria um jeito de fazer uma modificação nesse código para que a caixinha subisse sem travar o prog?

Mais uma vez obrigado pela atenção, inclusive pela resposta ao caso da modificação do tratamento de threads no VS 2008.

Valeu mesmo!!!  :)

E tenho certeza que vamos conseguir resolver isso.

E desculpa ae por qualquer coisa.  :)

0

Partilhar esta mensagem


Link para a mensagem
Partilhar noutros sites

Na sub backupThreadMain falta um Else:

' Chama o delegate
Private Sub backupThreadMain()
    If Me.InvokeRequired Then
        Me.Invoke(New myDelegate(AddressOf startBackUp))
    Else
        Me.startBackup()
    End If
End Sub

A semântica desta função é: se for preciso chamar invoke, ou seja, se o código não estiver a correr na thread GUI, chama o delegate através de Invoke, senão chama directamente a função.

  Com esse código eu fiz uma pequena modificação...

 Imports System.Threading

Public Class ThreadTest

' Um delegate é uma referência ou apontador a um
' método e como a thread em si não pode executar
' operações é necessário utiliza-lo
Delegate Sub myDelegate()

' Chama o delegate
Private Sub backupThreadMain()
    If Me.InvokeRequired Then
        Me.Invoke(New myDelegate(AddressOf startBackUp))
    End If
End Sub

' Inicia o backup
Private Sub startBackUp ()


  do until (TextBox1.Top > 100)

      TextBox1.Top = TextBox1.Top + 1
       Thread.Sleep(10)

  Loop

End Sub

' Código inicial do form
Private Sub frmBackUp_Load(ByVal sender As Object, ByVal e As System.EventArgs) Handles MyBase.Load

    ' Cria e inicia uma nova thread
    Dim bkThread As New Threading.Thread(AddressOf backupThreadMain)
    bkThread.Priority = ThreadPriority.Highest   ' Eu tentei por normal aqui mas naum adianta
    bkThread.IsBackground = True
    bkThread.Start()
End Sub

End Class

Esse código aqui também trava o programa, deixa só a caixinha de texto a subir e o form travado. :cheesygrin:

Quando sugeri que passasses o Do...Until para fora do delegate era o seguinte:

' Chama o delegate
Private Sub backupThreadMain()
    If Me.InvokeRequired Then
        do until (TextBox1.Top > 100)
            Me.Invoke(New myDelegate(AddressOf startBackUp))
            Sleep(10)
        Loop
    Else
        do until (TextBox1.Top > 100)
            Me.startBackup()
            Sleep(10)
        Loop
    End If
End Sub

' Inicia o backup
Private Sub startBackUp ()

    TextBox1.Top = TextBox1.Top + 1
    ' E aqui ficava o código para mover a 2ª caixa

End Sub

Eu pensava que o thread.sleep(x) deixava só a thread dormindo; o prog em sí, continuava rodando.

A função Thread.Sleep pára efectivamente a thread onde é chamado. Mas como a thread onde estavas a chamar a função era a thread que actualiza a GUI, esta fica sem resposta. Ao passares o Sleep para a outra thread, tudo o que a thread GUI faz é alterar as coordenadas da caixa de diálogo, e a GUI já não pára de responder.

Podem haver erros no código, não sou programador de VB e raramente alguma vez lhe mexi :) além de que não testei o código.

0

Partilhar esta mensagem


Link para a mensagem
Partilhar noutros sites

Hummmm.....

Estive a ler o código, acho que dará certo, quando chegar em casa testarei, e tenho quase certeza que marcarei o tópico como resolvido.

:)

  Mais uma vez obrigado por tudo parceroooo,  valeu mesmo!!!  :) :)

  Já já testo e posto. Valeu :P

0

Partilhar esta mensagem


Link para a mensagem
Partilhar noutros sites

Depois diz se está a funcionar!

Já agora porque usar threads para isto ? Porque não usas um simples timer ?

Exemplo:

Public Class Form1

    Private WithEvents tm As New Windows.Forms.Timer

    Private Sub tm_Tick(ByVal sender As Object, ByVal e As System.EventArgs) Handles tm.Tick
        If TextBox1.Top < 200 Then
            TextBox1.Top += 1
        End If
        If TextBox1.Top < 200 Then
            TextBox2.Top -= 1
        End If

        If TextBox1.Top <= 200 AndAlso TextBox1.Top >= 200 Then
            tm.Stop()
        End If
    End Sub


    Private Sub Button1_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles Button1.Click
        tm.Interval = 50
        tm.Start()
    End Sub

End Class

0

Partilhar esta mensagem


Link para a mensagem
Partilhar noutros sites

  Estava a usar as Threads para apenas "pegar o jeito", sou um pouco chucro para me atualizar.  :down:

  Era mais para estudos.

 

  Bom, mas enfim, muito obrigado mesmo pela atenção, e por tudo viu?

  Funcionou maravilhosamente bem, rs, do jeitinho que eu pretendia, testei no outro prog das duas TextBox e funcionou também, perfeitamente.

  Valeu mesmo hein???

  Quem sabe um dia eu não possa retribuir o favor?  :)

  Vou espiar o sitio para ver se consigo resolver a duvida de alguém, quem sabe?  :hmm: :ipool:

  Valeu aeee heim???

0

Partilhar esta mensagem


Link para a mensagem
Partilhar noutros sites

Crie uma conta ou ligue-se para comentar

Só membros podem comentar

Criar nova conta

Registe para ter uma conta na nossa comunidade. É fácil!


Registar nova conta

Entra

Já tem conta? Inicie sessão aqui.


Entrar Agora